1. 程式人生 > >KNIGHTS - Knights of the Round Table 圓桌騎士 點雙 + 二分圖判定

KNIGHTS - Knights of the Round Table 圓桌騎士 點雙 + 二分圖判定

~~~題面~~~

題解:

  考場上只想到了找點雙,,,,然後不知道怎麼處理奇環的問題。

  我們考慮對圖取補集,這樣兩點之間連邊就代表它們可以相鄰, 那麼一個點合法當且僅當有至少一個大小至少為3的奇環經過了它。

  觀察到只會出現一棵類似樹的結構 + t個相對獨立的環, 因為環肯定都是獨立出來的,所以可以不用管它。

  因此我們先找出所有點雙,然後判斷這個點雙內是否有奇環,用二分圖染色來判斷。如果有奇環,則說明這個點雙內的所有點都可以出現在一個奇環上,反之則都不會出現。

  所以我們只需要尋找一下點雙,然後判斷是否合法並加上相應貢獻即可。

  1 #include<bits/stdc++.h>
  2
using namespace std; 3 #define R register int 4 #define AC 1100 5 #define ac 11000000 6 7 int n, m, cnt, timer, ans; 8 int q[AC], top; 9 int q1[AC], head, tail; 10 int belong[AC], low[AC], color[AC], dfn[AC]; 11 bool z[AC][AC], vis[AC]; 12 13 inline int read() 14 { 15 int
x = 0;char c = getchar(); 16 while(c > '9' || c < '0') c = getchar(); 17 while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); 18 return x; 19 } 20 21 void pre() 22 { 23 n = read(), m = read(); 24 if(!n && !m) exit(0); 25 memset(belong, 0
, sizeof(belong)); 26 memset(vis, 0, sizeof(vis)); 27 memset(dfn, 0, sizeof(dfn)); 28 timer = cnt = ans = top = 0; 29 memset(z, 0, sizeof(z)); 30 int a, b; 31 for(R i = 1; i <= m; i ++) 32 { 33 a = read(), b = read(); 34 z[a][b] = z[b][a] = true; 35 } 36 for(R i = 1; i <= n; i ++) z[i][i] = true;//不能走自環 37 } 38 39 inline void upmin(int &a, int b){ 40 if(b < a) a = b; 41 } 42 43 bool check(int x)//檢查x所在點雙是否是一個二分圖 44 { 45 memset(color, 0, sizeof(color)); 46 head = tail = 0; 47 q1[++tail] = x, color[x] = 1; 48 while(head < tail) 49 { 50 x = q1[++head]; 51 for(R i = 1; i <= n; i ++) 52 { 53 if(!z[x][i] && belong[i] == cnt)//如果有邊並且在同一個點雙中 54 { 55 if(color[x] == color[i]) return false; 56 else if(!color[i]) q1[++tail] = i, color[i] = color[x] ^ 3; 57 } 58 } 59 } 60 return true; 61 } 62 63 void tarjan(int x, int fa)//求點雙聯通分量 64 { 65 low[x] = dfn[x] = ++ timer; 66 for(R i = 1; i <= n; i ++)//列舉邊 67 { 68 if(!z[x][i]) 69 { 70 if(!dfn[i]) 71 { 72 q[++top] = i;//將這個點加入棧 73 tarjan(i, x); 74 upmin(low[x], low[i]); 75 if(low[i] >= dfn[x])//這個點是割點 76 { 77 int tot = 2;//先加上割點和當前now的tot 78 belong[x] = ++ cnt;//先給這個割點臨時打上標記 79 while(q[top] != i) ++tot, belong[q[top --]] = cnt;//記錄下這個點所屬的點雙聯通分量 80 belong[q[top --]] = cnt; 81 if(!check(x))//不是二分圖,那麼這個bcc當中的點都是合法的 82 { 83 int b = top + tot - 1;//直接把剛取出來的點打上標記 84 vis[x] = true;//q[top] 不一定是割點 85 for(R i = top + 1; i <= b; i ++) vis[q[i]] = true; 86 } 87 } 88 } 89 else if(i != fa) upmin(low[x], dfn[i]); 90 } 91 } 92 } 93 94 void work() 95 { 96 while(1) 97 { 98 pre(); 99 for(R i = 1; i <= n; i ++) 100 if(!dfn[i]) q[++top] = i, tarjan(i, i);//將當前點加入棧 101 for(R i = 1; i <= n; i ++) if(!vis[i]) ++ans; 102 printf("%d\n", ans); 103 } 104 } 105 106 int main() 107 { 108 // freopen("in.in", "r", stdin); 109 work(); 110 // fclose(stdin); 111 return 0; 112 }
View Code