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

题目传送门
题意解析:题目给了你一个有限图,让你给这张图一次染色,每种色彩有种编号,每个点指向的点的颜色编号(设这个点的编号为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-8
using 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记录:
这里写图片描述

发布了48 篇原创文章 · 获赞 15 · 访问量 8749
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章