BZOJ1064 [Noi2008]假面舞會 【dfs】
題目
一年一度的假面舞會又開始了,棟棟也興致勃勃的參加了今年的舞會。今年的面具都是主辦方特別定制的。每個參加舞會的人都可以在入場時選擇一 個自己喜歡的面具。每個面具都有一個編號,主辦方會把此編號告訴拿該面具的人。為了使舞會更有神秘感,主辦方把面具分為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】