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} 1right)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%的数据1leq n,mleq 5 times 10^5
思路
对于每一条边,如果加入后无环,那么将其塞入树中,并标出每个点的深度与父亲。 如果是一条非树边,那么就暴力求出他们的LCA(直接选择深度大的往上跳),并且把路径上所有点用并查集缩起来,每个时刻上树上还没有被缩起来的边就是桥。时间复杂度为O(n alpha(n))
Code
代码语言:javascript复制#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 '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;
}