B 酱的无向图 题解

[mdx_warning]本题目有版权,禁止复制[/mdx_warning]

题目描述

B 酱有$n$个节点的向图,初始时图中没有边。他依次向图中加入了$m$条向边,并询问你加入每条边后图中桥的个数是多少。被删除后能使图中连通块个数增加的边就称为桥。注意图中可能会出现重边及负环。

输入格式

输入第一行为三个正整数$n,m, p, p $的含义将在输出格式中介绍。 接下来$ m $行,每行两个正整数$ u, v$,表示新加入的一条边。

输出格式

为减少输出量,设$ ans_i $表示加入第$ i $条边后图中桥的个数,请输出$\left(\prod_{i=1}^{m}\left(a n s_{i}+1\right)\right) \quad \bmod p$,其中$\Pi$表示连乘。

输入样例#1

4 4 233 1 2 2 3 3 4 1 4

输出样例#1

24

输入样例#2

6 7 233 6 5 1 2 3 2 1 2 4 6 4 5 1 1

输出样例#2

220

数据范围与约定

对于100%的数据$1\leq n,m\leq 5 \times 10^5$

思路

对于每一条边,如果加入后无环,那么将其塞入树中,并标出每个点的深度与父亲。 如果是一条非树边,那么就暴力求出他们的$LCA$(直接选择深度大的往上跳),并且把路径上所有点用并查集缩起来,每个时刻上树上还没有被缩起来的边就是桥。时间复杂度为$O(n \alpha(n))$

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include<algorithm>
#include<bitset>
#include<complex>
#include<deque>
#include<exception>
#include<fstream>
#include<functional>
#include<iomanip>
#include<ios>
#include<iosfwd>
#include<iostream>
#include<istream>
#include<iterator>
#include<limits>
#include<list>
#include<locale>
#include<map>
#include<memory>
#include<new>
#include<numeric>
#include<ostream>
#include<queue>
#include<set>
#include<sstream>
#include<stdexcept>
#include<streambuf>
#include<string>
#include<typeinfo>
#include<utility>
#include<valarray>
#include<vector>
#include<cctype>
#include<cerrno>
#include<cfloat>
#include<ciso646>
#include<climits>
#include<clocale>
#include<cmath>
#include<csetjmp>
#include<csignal>
#include<cstdarg>
#include<cstddef>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#define MAXN 5000010
#define int long long
using namespace std;
inline int read(){
int res=0,f=1;char ch=getchar();
while(ch<'0'ch>'9'){if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') res=res*10+ch-'0',ch=getchar();
return res*f;
}
inline void write(int x){
if(x<0) putchar('-'),x=-x;
if(x<10) putchar(x+'0');
else{
write(x/10);
putchar(x%10+'0');
}
}
int n,m,mod,ans=1,sum,las,num;
int fa[MAXN],u[MAXN],v[MAXN],g[MAXN],f[MAXN],vis[MAXN],def[MAXN],cnt;
int getfa(int x){
return x==f[x]?x:f[x]=getfa(f[x]);
}
int fir[MAXN],nxt[MAXN],son[MAXN],tot,w[MAXN];
void add(int x,int y,int z){
++tot;
nxt[tot]=fir[x];
fir[x]=tot;
son[tot]=y;
w[tot]=z;
}
void dfs(int x){//dfs求每一个点的深度与父亲
def[x]=def[fa[x]]+1;
f[x]=x;
for(int i=fir[x];i;i=nxt[i]){
int to=son[i];
if(to==x) continue
if(to==fa[x]) continue ;
fa[to]=x;
dfs(to);
}
}
signed main(){
n=read();m=read();mod=read();las=0;
for(int i=1;i<=n;i++) f[i]=i;
memset(fa,0,sizeof(fa));
cnt=0;
for(int i=1;i<=m;i++){
u[i]=read();v[i]=read();
if(getfa(u[i])==getfa(v[i])){
vis[i]=0;
}else{
vis[i]=1;
add(u[i],v[i],0);cnt++;
add(v[i],u[i],0);cnt++;
f[f[u[i]]]=v[i];
}
}
for(int i=1;i<=n;i++){
if(def[i]==0){
dfs(i);
}
}
for(int i=1;i<=m;i++){
if(vis[i]==1) sum++;//树边
else{//非树边
for(int x=getfa(u[i]),y=getfa(v[i]);x!=y;--sum,x=f[x]=getfa(fa[x])){//求LCA,并且用并查集缩起来
if(def[x]<def[y]) swap(x,y);//深度大的点向上跳
}
}
ans*=sum+1;ans%=mod;
}
write(ans);putchar('\n');
return 0;
}