BZOJ4116 : [Wf2015]Tours

将边集划分成若干极大不相交集合,满足每个简单环都可以由某些集合相加得到,则答案就是这些集合大小的$\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;
}

  

posted @ 2016-07-09 00:46  Claris  阅读(385)  评论(0编辑  收藏  举报