将边集划分成若干极大不相交集合,满足每个简单环都可以由某些集合相加得到,则答案就是这些集合大小的$\gcd$的约数。
对于一个简单环,上面的边一定不是桥边,而和它在一个集合的边肯定不在其他简单环上。因此删除它之后,这些边就从非桥边变成了桥边。
枚举每条非桥边跑Tarjan计算答案即可。
时间复杂度$O(m(n+m))$。
#include<cstdio> const int N=2005,BUF=21000; int n,m,i,x,y,now,ans,D,cut[N],g[N],v[N<<1],nxt[N<<1],ed=1; int q[N<<1],*st[N],*en[N],dfn[N],low[N],num;char Buf[BUF],*buf=Buf; inline void read(int&a){for(a=0;*buf<48;buf++);while(*buf>47)a=a*10+*buf++-48;}inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} void dfs(int x,int y){ dfn[x]=low[x]=++num; for(int*i=st[x];i<en[x];i++){ int u=(*i)&2047; if(u==y)continue; if(!dfn[u]){ dfs(u,x); if(low[x]>low[u])low[x]=low[u]; if(low[u]==dfn[u])cut[(*i)>>12]=1; }else if(low[x]>dfn[u])low[x]=dfn[u]; } } void tarjan(int x,int y){ dfn[x]=low[x]=++num; for(int*i=st[x];i<en[x];i++)if(((*i)>>12)!=D){ int u=*i&2047; if(u==y)continue; if(!dfn[u]){ tarjan(u,x); if(low[x]>low[u])low[x]=low[u]; if(low[u]==dfn[u]&&!cut[(*i)>>12])now++; }else if(low[x]>dfn[u])low[x]=dfn[u]; } } int gcd(int a,int b){return b?gcd(b,a%b):a;} int main(){ fread(Buf,1,BUF,stdin),read(n),read(m); for(i=1;i<=m;i++)read(x),read(y),add(x,y),add(y,x); for(i=1;i<=n;i++){ st[i]=q+num+1; for(x=g[i];x;x=nxt[x])q[++num]=x<<11|v[x]; en[i]=q+num+1; } for(num=0,i=1;i<=n;i++)if(!dfn[i])dfs(i,0); for(D=1;D<=m;D++)if(!cut[D]){ for(now=i=1,num=0;i<=n;i++)dfn[i]=0; for(i=1;i<=n;i++)if(!dfn[i])tarjan(i,0); if(!ans)ans=now;else ans=gcd(ans,now); } for(i=1;i<=ans;i++)if(ans%i==0)printf("%d%c",i,i<ans?‘ ‘:‘\n‘); return 0; }
时间: 2024-10-26 16:18:34