2 B 酱的无向图
2.1 题目描述
B 酱有个 n 个节点的无向图,初始时图中没有边。他依次向图中加
了 m 条无向边,并询问你加入每条边后图中桥的个数是多少。被删除后
能使图中连通块个数增加的边就称为桥。注意图中可能会出现重边及自环。
2.2 输入格式
输入第1行为三个正整数 n, m, p,p 的含义将在输出格式中介绍。
接下来 m 行,每行两个正整数 u, v,表示新加入的一条边。
2.3 输出格式
为减少输出量,设 ansi 表示加入第 i 条边后图中桥的个数,请输出
(Πmi=1(ansi+1))modp
(
Π
i
=
1
m
(
a
n
s
i
+
1
)
)
mod
p
,其中
Π
Π
表示连乘。
2.4 输入样例一
4 4 233
1 2
2 3
3 4
1 4
2.5 输出样例一
24
2.6 输入样例二
6 7 233
6 5
1 2
3 2
1 2
4 6
4 5
1 1
2.7 输出样例二
220
2.8 样例解释
对于样例一,实际答案为 1,2,3,0。
对于样例二,实际答案为 1,2,3,2,3,1,1。
2.9 数据范围与约定
测试点编号 n, m
1,2≤300
1
,
2
≤
300
3,4,5≤3000
3
,
4
,
5
≤
3000
6≤1∗105
6
≤
1
∗
10
5
7≤2∗105
7
≤
2
∗
10
5
8≤3∗105
8
≤
3
∗
10
5
9,10≤5∗105
9
,
10
≤
5
∗
10
5
对于
100%
100
%
的数据,保证
1≤u,v≤n,1≤p≤109
1
≤
u
,
v
≤
n
,
1
≤
p
≤
10
9
。
对于所有不会构成环的边,读入之后建树。建树时维护深度和父节点。
对于所有会构成环的边,暴力向上爬,把环中所有点用并查集缩点。
树上没有被缩起来的点即为桥。
由于每条边只会被缩一次,所以时间复杂度为 O(n∗α(n)) O ( n ∗ α ( n ) )
#include<cstdio>
#include<cstring>
#include<algorithm>
#define max(x,y) ((x)>(y) ? (x) : (y))
#define min(x,y) ((x)<(y) ? (x) : (y))
#define N 500010
#define ll long long
using namespace std;
inline int getint() {
char ch;
int p=0,t;
for(ch=getchar();ch!='-' && !(ch>='0' && ch<='9');ch=getchar());
if(ch=='-') t=-1,ch=getchar();
else t=1;
for(;ch>='0' && ch<='9';ch=getchar()) {
p=p*10+ch-48;
}
return t*p;
}
struct Edge {
int to,ne;
} e[N*3];
ll acht,ans;
int n,m,p,f[N],flag[N],g[N],d[N],u[N],v[N],cnt,fa[N];
inline int getf(int x) {
return (x==f[x] ? x : f[x]=getf(f[x]));
}
void dfs(int u) {
d[u]=d[fa[u]]+1;
f[u]=u;
for(int i=g[u];i!=-1;i=e[i].ne) {
if(e[i].to==fa[u]) continue;
fa[e[i].to]=u;
dfs(e[i].to);
}
}
int main()
{
// freopen("graph.in","r",stdin);
// freopen("graph.out","w",stdout);
n=getint();
m=getint();
p=getint();
for(int i=1;i<=n;i++) {
f[i]=i;
}
memset(g,-1,sizeof g);
memset(fa,0,sizeof fa);
cnt=0;
memset(flag,false,sizeof flag);
for(int i=1;i<=m;i++) {
u[i]=getint();
v[i]=getint();
if(getf(u[i])!=getf(v[i])) {
f[f[u[i]]]=v[i];
flag[i]=true;
e[cnt]=(Edge){v[i],g[u[i]]};
g[u[i]]=cnt++;
e[cnt]=(Edge){u[i],g[v[i]]};
g[v[i]]=cnt++;
}
}
for(int i=1;i<=n;i++) { //用所有树边建树
if(!d[i]) {
dfs(i);
}
}
acht=1;
for(int i=1;i<=m;i++) {
if(flag[i]) ++ans;
else {
for(int x=getf(u[i]),y=getf(v[i]);x!=y;x=f[x]=getf(fa[x]),--ans) {
if(d[x]<d[y]) {
swap(x,y); //每次让深度大的向上爬,把环内的节点并在一起。
}
}
}
(acht*=ans+1)%=p;
// printf("%lld\n",ans);
}
printf("%lld\n",acht);
return 0;
}