bzoj 1064: [Noi2008]假面舞会

题目
题解
可以把整个图分为环和链来考虑。
对于链的情况, k 的最小值就是链的长度,最大值就是所有的链接起来。
对于环的情况, k 的最大值就是环的大小,环大小的约数k也可以取。
现在问题就变成了怎样求环。如果是无向图的话,直接随便找个点 d f s 就行了。但是现在是有向图,所以需要变成无向图。将原图中的每一条边加上一个 1 的权值,然后再新建一条权值是 1 的反向边就行了。

#include<bits/stdc++.h>
using namespace std;
const int N=100002;
struct node{
    int to,ne,w;
}e[20*N];
int h[N],dis[N],mx[N],mn[N],ans1,ans2,u,v,i,f[N],tot,fl[N],n,m,rx,ry,now;
inline char gc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
#define gc getchar
inline int read(){
    int x=0,fl=1;char ch=gc();
    for (;ch<48||ch>57;ch=gc())if(ch=='-')fl=-1;
    for (;48<=ch&&ch<=57;ch=gc())x=(x<<3)+(x<<1)+(ch^48);
    return x*fl;
}
int find(int x){
    return x==f[x]?x:f[x]=find(f[x]);
}
void add(int x,int y,int z){
    e[++tot]=(node){y,h[x],z};
    h[x]=tot;
}
void dfs(int u){
    fl[u]=0;
    mx[now]=max(mx[now],dis[u]);
    mn[now]=min(mn[now],dis[u]);
    for (int i=h[u],v;i;i=e[i].ne)
        if (fl[v=e[i].to]){
            dis[v]=dis[u]+e[i].w;
            dfs(v);
        }else ans1=__gcd(ans1,abs(dis[u]+e[i].w-dis[v]));
}
int main(){
    n=read();m=read();
    for (i=1;i<=n;i++) f[i]=i;
    for (i=1;i<=m;i++){
        u=read(),v=read();
        add(u,v,1);add(v,u,-1);
        rx=find(u);ry=find(v);
        if (rx!=ry) f[rx]=ry;
    }
    memset(fl,1,sizeof(fl));
    memset(mn,63,sizeof(mn));
    for (i=1;i<=n;i++)
        if (fl[i]) now=find(i),dfs(i);
    for (i=3;i<=ans1 && !ans2;i++)//环
        if (!(ans1%i)) ans2=i;
    if (!ans1){//链
        ans2=3;
        for (i=1;i<=n;i++)
            if (f[i]==i) ans1+=mx[i]-mn[i]+1;
    }
    if (ans1<3) ans1=ans2=-1;
    printf("%d %d",ans1,ans2);
}

猜你喜欢

转载自blog.csdn.net/xumingyang0/article/details/81015268