[bzoj-1064] [Noi2008]假面舞会 题解

来源:互联网 发布:cda数据分析师教材 编辑:程序博客网 时间:2024/04/28 04:39

题目传送门
题意解析:题目给了你一个有限图,让你给这张图一次染色,每种色彩有种编号,每个点指向的点的颜色编号(设这个点的编号为i)应该是i+1(当色彩有m种,当前i=m时,指向的点的色彩编号应该为1)。最后问色彩的种类最多和最少是多少(最后的色彩数应该>=3)。


My opinion:开始只会强行暴力染色,然后想了想可不可以二分色彩数,然后判读,却发现如果x可以,但是x+1不一定可以(手动绝望)。然后去手画了几种情况,既然是一张图,那就要考虑是否有环。
先考虑没环的情况(也就是一棵树):
这里写图片描述
很明显,随便你怎么填颜色,最大值就是树的层数,最小值就是3(因为颜色数>=3嘛)。
然后是有环的情况:
这里写图片描述
也可以看出,在一个环当中,颜色种数就是环中点的个数的约数。
当然,还有一种神奇的环,就是把环中的一些边反向。
这里写图片描述
其实也是一样的,所以这张有向图就可以看成一张无向图。
原图的边权值为1,反向边的权值为-1。
总结:
1、建图。
2、跑bfs找出是否有环,并且记录个数。
3、分有环和无环的情况讨论。
(这里的有环无环是指变成无向图之后的)


代码:

#include<iostream>#include<cmath>#include<cstdio>#include<cstring>#include<algorithm>#include<queue>#define rep(i,a,n) for (int i=a;i<=n;i++)#define per(i,a,n) for (int i=a;i>=n;i--)#define Clear(a,x) memset(a,x,sizeof(a))#define ll long long#define INF 2000000000#define eps 1e-8using namespace std;ll read(){    ll x=0,f=1;    char ch=getchar();    while (ch<'0'||ch>'9') f=ch=='-'?-1:f,ch=getchar();    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();    return x*f;}const int maxn=100005,maxm=1000005;int n,m,len,d;int vet[maxm<<1],Next[maxm<<1],road[maxm<<1],head[maxn];bool flag[maxn];int dis[maxn];void add(int u,int v,int w){    vet[++len]=v;    Next[len]=head[u];    head[u]=len;    road[len]=w;}int gcd(int a,int b){    return !b?a:gcd(b,a%b);}int bfs(int st){    int maxdis=0,mindis=0;    queue<int> q;    while (!q.empty()) q.pop();    dis[st]=0;    flag[st]=1;    q.push(st);    while (!q.empty()){        int u=q.front();        q.pop();        for (int e=head[u];e!=-1;e=Next[e]){            int v=vet[e];            if (flag[v]){                d=gcd(d,dis[u]+road[e]-dis[v]);                continue;            }            flag[v]=1;            dis[v]=dis[u]+road[e];            maxdis=max(maxdis,dis[v]);            mindis=min(mindis,dis[v]);            q.push(v);        }    }    return maxdis-mindis+1;}int main(){    n=read(),m=read();    int sum=0;    Clear(head,-1);    rep(i,1,m){        int u=read(),v=read();        add(u,v,1);        add(v,u,-1);    }    rep(i,1,n)        if (!flag[i])            sum+=bfs(i);    if (d<0) d=-d;    if (d){        if (d<3){            puts("-1 -1");            return 0;        }        int ans=3;        while (ans<d&&d%ans) ans++;        printf("%d %d\n",d,ans);        return 0;    }else if (sum<3){        puts("-1 -1");        return 0;    }    printf("%d 3\n",sum);    return 0;} 

附上AC记录:
这里写图片描述

原创粉丝点击