Codechef Counting the important pairs 树上差分

传送门

题意:给出一个$N$个点、$M$条边的无向连通图,求有多少组无序数对$(i,j)$满足:割掉第$i$条边与第$j$条边之后,图变为不连通。$N \leq 10^5 , M \leq 3 \times 10^5$



竟然随机化,歪果仁的思想好灵活qwq肯定是数据结构做多了

看起来很像割边,考虑$tarjan$,但是边散连通分量并不是很好实现,而且有很多特殊情况需要判断,所以我们考虑另外的算法

考虑$tarjan$时建出的一棵树。对于它来说,在一个端点在其下方、另一个端点在其上方的的返祖边可以成为它的依靠,因为割掉它,这一条依靠边可以代替它的功能。而对于一条返祖边来说,割掉对于树边是没有影响的,我们就定义它自己为自己的依靠。

这样每一条边都有自己的依靠集合。考虑两条依赖集合相同的边,将它们割掉之后,中间一段的点就会因为上下都没有额外的依靠而使得图不连通。而对于一条依赖集合为空的边(即割边),它选择任何边都可以加入贡献。

所以我们现在需要考虑如何给某一段边加入贡献。然后CC的题解给出的玄学办法是:随机化+树上差分+XOR

我们考虑将给一条返祖边定权值为一个$random$出来的值$t$,然后把所有依靠它的边的依靠集合异或上这个值,这个可以树上差分去做。这样所有的依靠集合就变成了一个数。然后我们判断两条边的依靠集合对应的数是否相等即可。

Because 2^64 is large enough comparing to the range of N and M, don‘t worry about the probability. :) You will get AC if you implemented correctly.——原题题解

然而我$rand() WA$了好几发qwq

如果随机数种子出了问题的话就看下面这种玄学rand好了

 1 #include<bits/stdc++.h>
 2 #define CC
 3 using namespace std;
 4
 5 inline int read(){
 6     int a = 0;
 7     bool f = 0;
 8     char c = getchar();
 9     while(c != EOF && !isdigit(c)){
10         if(c == ‘-‘)
11             f = 1;
12         c = getchar();
13     }
14     while(c != EOF && isdigit(c)){
15         a = (a << 3) + (a << 1) + (c ^ ‘0‘);
16         c = getchar();
17     }
18     return f ? -a : a;
19 }
20
21 const int MAXN = 100010;
22 struct Edge{
23     int end , upEd;
24 }Ed[MAXN * 6];
25 int head[MAXN] , num[MAXN] , dep[MAXN] , fa[MAXN] , N , cntEd , cnt;
26 unsigned long long point[MAXN] , forS[MAXN * 3];
27 bool vis[MAXN];
28
29 #define ll unsigned long long
30 inline ll rp(){return (1ll*rand())^(1ll*rand())<<15^(1ll*rand())<<30^(1ll*rand())<<45^(1ll*rand())<<60;}
31
32 inline void addEd(int a , int b){
33     Ed[++cntEd].end = b;
34     Ed[cntEd].upEd = head[a];
35     head[a] = cntEd;
36 }
37
38 void dfs1(int x , int t){
39     fa[x] = t;
40     dep[x] = dep[fa[x]] + 1;
41     vis[x] = 1;
42     for(int i = head[x] ; i ; i = Ed[i].upEd)
43         if(!vis[Ed[i].end])
44             dfs1(Ed[i].end , x);
45         else
46             if(dep[Ed[i].end] > dep[x]){
47                 long long t = rp();
48                 point[x] ^= t;
49                 point[Ed[i].end] ^= t;
50                 forS[++cnt] = t;
51             }
52 }
53
54 void dfs2(int x){
55     for(int i = head[x] ; i ; i = Ed[i].upEd)
56         if(fa[Ed[i].end] == x){
57             dfs2(Ed[i].end);
58             point[x] ^= point[Ed[i].end];
59         }
60     if(x != 1)
61         forS[++cnt] = point[x];
62 }
63
64 int main(){
65 #ifdef CC
66     freopen("TAPAIR.in" , "r" , stdin);
67     freopen("TAPAIR.out" , "w" , stdout);
68 #endif
69
70     N = read();
71     int M = read();
72     for(int i = 1 ; i <= M ; i++){
73         int a = read() , b = read();
74         addEd(a , b);
75         addEd(b , a);
76     }
77     dfs1(1 , 0);
78     dfs2(1);
79     sort(forS + 1 , forS + cnt + 1);
80     int p = 1;
81     while(p <= cnt && forS[p] == 0)
82         p++;
83     long long ans = (p - 1) * (long long)(p - 2) / 2 + (p - 1) * (M - p + 1);
84     while(p <= cnt){
85         int beg = p;
86         while(p <= cnt && forS[p] == forS[beg])
87             p++;
88         ans += (long long)(p - beg) * (p - beg - 1) / 2;
89     }
90     cout << ans;
91     return 0;
92 }

原文地址:https://www.cnblogs.com/Itst/p/9858885.html

时间: 2024-10-18 19:24:41

Codechef Counting the important pairs 树上差分的相关文章

Counting The Important Pairs CodeChef - TAPAIR

https://vjudge.net/problem/CodeChef-TAPAIR 合法的删除方法: 第一种:桥边与其余任意边(1)桥*(桥-1)/2(两条桥边)(2)桥*(m-桥)(桥边+其他边)第二种:两条非桥边:一定在同一个边双内对每一个边双求dfs树(1)两条树边(定义覆盖:反向边(a,b)覆盖了dfs树上a到b路径中每一条边)显然,任意边覆盖的路径中都是深度递减/递增的一些点如果两条树边被完全相同的边集覆盖,那么显然(感性理解)它们处在相同的环的中,因此同时去掉能让这些环断开两个口子

P3128 [USACO15DEC]最大流Max Flow (树上差分)

题目描述 Farmer John has installed a new system of N-1N−1 pipes to transport milk between the NN stalls in his barn (2 \leq N \leq 50,0002≤N≤50,000), conveniently numbered 1 \ldots N1…N. Each pipe connects a pair of stalls, and all stalls are connected t

[填坑]树上差分 例题:[JLOI2014]松鼠的新家(LCA)

今天算是把LCA这个坑填上了一点点,又复习(其实是预习)了一下树上差分.其实普通的差分我还是会的,树上的嘛,也是懂原理的就是没怎么打过. 我们先来把树上差分能做到的看一下: 1.找所有路径公共覆盖的边 例题:[NOIP2015]运输计划 (然而我还没过就先不讲了) 反正就是中间有一步要求一条边被所有计划公共覆盖. 那么怎么求它呢?暴力(滚粗).我们有一个非常好的方法就是树上差分(记录tmp为差分数组) 询问操作为从叶子节点的权值向上累加到root 在一条路径u→ v,如果tmp[u]++,那么我

【BZOJ-4326】运输计划 树链剖分 + 树上差分 + 二分

4326: NOIP2015 运输计划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 703  Solved: 461[Submit][Status][Discuss] Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球.小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui

【COGS 2434】 暗之链锁 树上差分+LCA

差分就是把一个值拆成许多差的和如 1 2 4 6 9 那么 把这个东西拆成 1 1 2 2 3 就是了,当然也可以理解为对一个问题分解为多个子问题并对其进行操作来得到原问题的答案. 树上差分就更玄妙了,它既可以把原问题拆成他到根节点的所有点,也可以拆成子树,拆成子树的话修改一个点影响的是他到根的路径上所有点,根据这个我们可以再加上LCA来解决许多问题. 这道题:I. 我们可以看出我们可以把它转化成一棵有根树,那么两部分一定是一个子树和其他 II. 那些虚边,都是砍断实边之后的藕断丝连,至于如何计

【CF739B】Alyona and a tree(树上差分,二分,树形DP)

题意:给出一棵有根树,树上每个点.每条边都有一个权值. 现在给出"控制"的定义:对一个点u,设点v在其子树上,且dis(u,v)≤av,则称u控制v. 要求求出每个点控制了多少个点 n (1?≤?n?≤?2·105).  (1?≤?ai?≤?109) 1?≤?pi?≤?n, 1?≤?wi?≤?109) 思路:在学校CF有时上不去不知道为什么 对于确定的点i,计算它对哪些点有贡献 dis[i]-dis[u]<=a[i] dis[u]<=a[i]-dis[i]满足二分性 倍增枚

XJOI CBH的发展计划(树上差分)

这是qzh的第二题 题目大意: 给你一棵树和一些连接祖先和孩子的边(非树枝边,类似于有向图返祖边) 让你求出删掉其中一条树枝边和一条非树枝边使图不联通的方案数 我们思考对树枝边统计答案 如图 对于红边统计答案,它的子树中有一条向外连的边,必须删掉红边和这条边才能使图不连通,所以这条树枝边贡献为1 如图 对于红边统计答案,它的子树中有0条向外连的边,删掉红边和任意一条虚线边能使图不连通,所以这条树枝边贡献为非树枝边的数量 如图 对于红边统计答案,它的子树中有两条及以上向外连的边,删掉红边和任意一条

BZOJ 4326 NOIP2015 运输计划 (二分+树上差分)

题意:中文题. 析:首先二分是很容易想出来的,然后主要是判断这个解合不合法,先二分答案 mid,因为有 m 个计划,所以只要添加虫洞的肯定是所有的时间长于 mid 的计划 中,也就是是那些的共同边,这个就可以用树上差分来做了,假设 s 到 t,那么让in[s]++,in[t]++,in[lca(s, t)] -= 2,其中in 表示的是 该结点与其父结点的边的计数,最后再跑一次dfs,把所有的权值都累加上去,这样就能知道哪些是共同的边了. 代码如下: #pragma comment(linker

BZOJ 3631 [JLOI2014]松鼠的新家 | 树上差分

链接 BZOJ 3631 题解 看起来是树剖?实际上树上差分就可以解决-- 当要给一条路径(u, v) +1的时候,给d[u] += 1, d[v] += 1, d[lca(u, v)] -= 1, d[fa[lca(u, v)]] -= 1. 注意这道题中路径的终点是不 +1的. #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <q