首页 > 代码库 > [BZOJ]1064: [Noi2008]假面舞会

[BZOJ]1064: [Noi2008]假面舞会

题目大意:n个人,k种假面,每人戴一种,戴第i种的可以看见第i+1种,戴第k种的可以看见第1种,给出m条关系表示一个人可以看到另一个人,问k可能的最大值和最小值。(n<=100,000,m<=1,000,000)

思路:染色,若点i颜色为ci,就把点i能到的点染成ci+1,能到点i的点染成ci-1,如果染之前已经染过了,设要染的点为j,则cj和ci+1(-1)模k同余,若cj不等于ci+1(-1),则k必然为|ci+1(-1)-cj|的因子,取gcd即可。若没有出现这种情况,最大答案为各连通块最长链的和。(稍微卡了个常卡到rank1 233)

#include<cstdio>
#include<algorithm>
using namespace std;
char B[1<<21],*S=B,C;int X;
inline int read()
{
    while((C=*S++)<0||C>9);
    for(X=C-0;(C=*S++)>=0&&C<=9;)X=(X<<3)+(X<<1)+C-0;
    return X;
}
#define r register int
#define MN 100000
#define MM 1000000
struct edge{int nx,t;}e[MM*2+5];
int h[MN+5],rh[MN+5],en,c[MN+5],ans,mn,mx;
inline void ins(int*h,int x,int y){e[++en]=(edge){h[x],y};h[x]=en;}
inline int gcd(int x,int y){return y?gcd(y,x%y):x;}
inline int z(int x){return x<0?-x:x;}
void dfs(int x)
{
    for(r i=h[x];i;i=e[i].nx)
        c[e[i].t]?ans=gcd(ans,z(c[e[i].t]-c[x]-1)):
            (mx=max(mx,c[e[i].t]=c[x]+1),dfs(e[i].t),0);
    for(r i=rh[x];i;i=e[i].nx)
        c[e[i].t]?ans=gcd(ans,z(c[e[i].t]-c[x]+1)):
            (mn=min(mn,c[e[i].t]=c[x]-1),dfs(e[i].t),0);
}
int main()
{
    fread(B,1,1<<21,stdin);
    int n,m,x,y,i,s=0;
    n=read();m=read();
    while(m--)x=read(),ins(h,x,y=read()),ins(rh,y,x);
    for(i=1;i<=n;++i)if(!c[i])c[i]=mn=mx=n,dfs(i),s+=mx-mn+1;
    if(ans)
    {
        if(ans<3)return 0*puts("-1 -1");
        for(i=3;i<=ans;++i)if(ans%i==0)break;
        return 0*printf("%d %d\n",ans,i);
    }
    printf("%d %d\n",s<3?-1:s,s<3?-1:3);
}

 

[BZOJ]1064: [Noi2008]假面舞会