[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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章