NOI 2008 假面舞會
阿新 • • 發佈:2018-12-17
如果沒有環那麼最大的面具的種類為所有聯通塊最長鏈之和、最小為3,如果最長鏈之和<3,則無解;
如果有環找出每個環的結點個數=、=面具種類數為所有環的結點個數的最大公約數x。
為什麼呢
一個環1->2,2->3,3->4,4->5,5->6,6->1;那麼可能的面具數為6或3種,是6的約數。
如果公約數小於3無解,大於3有解,最小的面具種數一定是>=3,<=x的一個x的約數。
那麼最長鏈怎麼求呢? 3->2->1->4->5;
如果列舉的話第一個是1,要求到最長鏈要列舉到3。。。。我們可以建立邊的時候1->4這條邊分為1->4為權值為1的邊,4->1為權值為-1的邊。那麼列舉到1時,從1開始dfs,求每個點的dis,dis[3] = -2,dis[5] = 2;那麼最長鏈的大小為dis[5] - dis[3] + 1;即遍歷一次,最大的dis - 最小的dis +1;
那麼環的個數怎麼求呢?
tarjian?不行啊比如1->2->3.然而還有1->3這條邊,然而這是個環,按以上建圖法,tarjian回把它算為一個大小為3的環。。。然而這個環是不合法的。。。。所以我們採用dfs,如果沒點v遍歷過更新dis,如果遍歷過環的大小為abs(dis[x] + tow - dis[v])(v是從x變了過來的,x->v這條邊的邊權為tow; )
#include<iostream> #include<cstdio> #include<cmath> using namespace std; int n, m; int tov[2000005],h[100005],tp, nex[2000005], tow[2000005]; int vis[100005], dis[100005], ma, vis1[100005], maa, mii,dis1[100005]; void read(int &x) { x = 0; char c = getchar(); while(c < '0' || c > '9') { c = getchar(); } while( c >= '0' && c <= '9') { x = 10 * x + c -'0'; c = getchar(); } } int gcd(int n, int m) { if(m == 0) return n; return gcd(m, n%m); } void add(int x,int y,int w) { tp ++; nex[tp] = h[x]; tow[tp] = w; h[x] = tp; tov[tp] = y; } void dfs(int x, int fa) { for(int i = h[x]; i; i = nex[i]) { int v = tov[i]; if(v == fa) continue; if(vis[v] == 1) { int ha = dis[v] - (dis[x] + tow[i]); if(ha < 0) ha = (-1) * ha; if(ma == 0) ma = ha; else { if(ha != 0) { ma = gcd(ma,ha); } } } else { vis[v] = 1; dis[v] = dis[x] + tow[i]; dfs(v,x); } } } void dfs1(int x, int fa) { for(int i = h[x]; i; i = nex[i]) { int v = tov[i]; if(vis1[v] == 1 || v == fa) continue; maa = max(maa , dis1[x] + tow[i]); mii = min(mii , dis1[x] + tow[i]); dis1[v] = dis1[x] + tow[i]; vis1[v] = 1; dfs1(v,x); } } int main() { read(n); read(m); for(int i = 1; i <= m; i++) { int x,y; read(x); read(y); add(x,y,1); add(y,x,-1); } for(int i = 1; i <= n; i++) { if(vis[i] == 0) { vis[i] = 1; dfs(i,i); } } if(ma == 0) { int zm = 0; for(int i = 1; i <= n; i++) { if(vis1[i] == 0) { maa = 0, mii = 0; vis1[i] = 1; dfs1(i,i); zm = zm + (maa - mii + 1); } } if(zm < 3) { printf("-1 -1"); return 0; } printf("%d 3",zm); } else { if(ma < 3) { printf("-1 -1"); return 0; } int mi = 0; for(int i = 3; i <= ma; i++) { if(ma % i == 0) { mi = i; break; } } printf("%d %d",ma,mi); } return 0; }