1. 程式人生 > >BZOJ1064 [Noi2008]假面舞會 【dfs】

BZOJ1064 [Noi2008]假面舞會 【dfs】

人的 spa clu 結果 最大 文件中 gcd 描述 UC

題目

一年一度的假面舞會又開始了,棟棟也興致勃勃的參加了今年的舞會。今年的面具都是主辦方特別定制的。每個參加舞會的人都可以在入場時選擇一 個自己喜歡的面具。每個面具都有一個編號,主辦方會把此編號告訴拿該面具的人。為了使舞會更有神秘感,主辦方把面具分為k (k≥3)類,並使用特殊的技術將每個面具的編號標在了面具上,只有戴第i 類面具的人才能看到戴第i+1 類面具的人的編號,戴第k 類面具的人能看到戴第1 類面具的人的編號。 參加舞會的人並不知道有多少類面具,但是棟棟對此卻特別好奇,他想自己算出有多少類面具,於是他開始在人群中收集信息。 棟棟收集的信息都是戴第幾號面具的人看到了第幾號面具的編號。如戴第2號面具的人看到了第5 號面具的編號。棟棟自己也會看到一些編號,他也會根據自己的面具編號把信息補充進去。由於並不是每個人都能記住自己所看到的全部編號,因此,棟棟收集的信 息不能保證其完整性。現在請你計算,按照棟棟目前得到的信息,至多和至少有多少類面具。由於主辦方已經聲明了k≥3,所以你必須將這條信息也考慮進去。

輸入格式

第一行包含兩個整數n, m,用一個空格分隔,n 表示主辦方總共準備了多少個面具,m 表示棟棟收集了多少條信息。接下來m 行,每行為兩個用空格分開的整數a, b,表示戴第a 號面具的人看到了第b 號面具的編號。相同的數對a, b 在輸入文件中可能出現多次。

輸出格式

包含兩個數,第一個數為最大可能的面具類數,第二個數為最小可能的面具類數。如果無法將所有的面具分為至少3 類,使得這些信息都滿足,則認為棟棟收集的信息有錯誤,輸出兩個-1。

輸入樣例

6 5

1 2

2 3

3 4

4 1

3 5

輸出樣例

4 4

提示

100%的數據,滿足n ≤ 100000, m ≤ 1000000。

題解

根據題目的描述,所有人的關系形成一種環狀關系
假若給出的關系中存在環,那麽環長一定是k的倍數
設所有環長的gcd為x,此時答案為[x大於3的因子,x]

假若沒有環,結果就是所有最長鏈之和

現在問題是如何求環以及最長鏈
有一種dfs的方法很厲害
我們將原邊賦值為1,建一個反邊賦值為-1,這樣就構造出了一個類似無向圖的東西,我們就可以從一個點出發訪問整個聯通塊
由於-1的存在,走反邊會導致負值,走正邊會形成正值,這樣兩點間長度就可以用差來求出
跑dfs時,遇到了返祖邊,則形成環,統計答案,同時統計這個聯通塊的最小權值和最大權值,只差 + 1即為最長鏈長度

#include<iostream>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int #define REP(i,n) for (int i = 1; i <= (n); i++) #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) #define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts(""); using namespace std; const int maxn = 100005,maxm = 2000005,INF = 1000000000; inline int read(){ int out = 0,flag = 1; char c = getchar(); while (c < 48 || c > 57) {if (c == ‘-‘) flag = -1; c = getchar();} while (c >= 48 && c <= 57) {out = (out << 3) + (out << 1) + c - ‘0‘; c = getchar();} return out * flag; } int h[maxn],ne = 2; struct EDGE{int to,nxt,w;}ed[maxm]; void build(int u,int v){ ed[ne] = (EDGE){v,h[u],1}; h[u] = ne++; ed[ne] = (EDGE){u,h[v],-1}; h[v] = ne++; } int n,m,f[maxn],vis[maxn],pre[maxn],gmax[maxn],gmin[maxn],now; int ansl,ansr; int gcd(int a,int b){return b ? gcd(b,a % b) : a;} int find(int u){return u == pre[u] ? u : pre[u] = find(pre[u]);} void dfs(int u,int last){ gmax[now] = max(gmax[now],f[u]); gmin[now] = min(gmin[now],f[u]); vis[u] = true; Redge(u) if ((k ^ 1) != last){ if (!vis[to = ed[k].to]) f[to] = f[u] + ed[k].w,dfs(to,k); else { ansr = gcd(abs(f[u] + ed[k].w - f[to]),ansr); //printf("%d to %d\n",u,to); } } } int main(){ n = read(); m = read(); int a,b,fa,fb; for (int i = 1; i <= n; i++) pre[i] = i; while (m--){ a = read(); b = read(); build(a,b); fa = find(a); fb = find(b); if (fa != fb) pre[fb] = fa; } for (int i = 1; i <= n; i++) if (!vis[i]) now = find(i),dfs(i,0); if (ansr){ for (int i = 3; i <= ansr; i++) if (ansr % i == 0){ansl = i; break;} if (ansl < 3) puts("-1 -1"); else printf("%d %d\n",ansr,ansl); }else { for (int i = 1; i <= n; i++) if (find(i) == i) ansr += (gmax[i] - gmin[i] + 1); if (ansr < 3) puts("-1 -1"); else printf("%d 3\n",ansr); } return 0; }

BZOJ1064 [Noi2008]假面舞會 【dfs】