Gym - 100886B 2015-2016 Petrozavodsk Winter Training Camp, Saratov SU Contest B - Game on Bipartite
阿新 • • 發佈:2018-11-05
題目有很強的結論,看程式碼應該可以知道結論是什麼
但是想了兩天還是沒有想到一個嚴格的證明
大概思路:
首先邊的奇偶影響答案(走過去優就繼續拓展,不優就走回來)
轉化為01問題
考慮消元的過程中,對面的一個點會發生什麼
如果這個點可以成為主元,說明從這個點出發一定會有奇數長度的路徑(即便是雙方交替進行)
↑感性認識一下感覺非常有道理,然而並不會證(sad
然後基於這個結論就可以做了,把其他所有點的出邊情況送進去消元,消完後再把起點也丟進去判斷是否會被消掉
因為這是公平博弈,所以可以用相同方法判斷對方在某個點的勝負,故可以產生方案
UPD:
似乎是後手只要不斷把先手往基上拉,先手就會輸,所以要看起點能不能被消掉
先手不往基上走為啥會贏我沒搞清楚,感覺與行列消元得到的主元相同有聯絡
Thx to yjq
#include<stdio.h> #include<cstring> #include<cstdlib> #define N 51 typedef long long ll; int n,m,e,v,edg[N][N],d1[N],d2[N]; ll row[N],col[N],pivot[N]; inline bool add(ll mask) { for (int i=1;i<N;i++) if (mask>>i&1) { if (pivot[i]) mask^=pivot[i]; else {pivot[i]=mask;return 1;} } return 0; } inline void go(int t) { printf("%d\n",t); fflush(stdout); --edg[v][t]; --d1[v]; row[v]^=1LL<<t; col[t]^=1LL<<v; if (--d2[t]==0) { puts("Player 1 wins"); exit(0); } scanf("%d",&v); --edg[v][t]; --d1[v],--d2[t]; row[v]^=1LL<<t; col[t]^=1LL<<v; } inline bool check(int y) { memset(pivot,0,sizeof(pivot)); for (int i=1;i<=m;i++) if (i!=y) add(col[i]); return !add(col[y]^(1LL<<v)); } inline void win() { while (1) { for (int i=1;i<=m;i++) if (edg[v][i] && check(i)) {go(i);break;} } } inline void lost() { while (1) { bool flag=1; for (int i=1;i<=m;i++) if (edg[v][i]) go(i),flag=0; if (flag) {puts("Player 2 wins");exit(0);} continue; } } int main() { scanf("%d%d%d%d",&n,&m,&e,&v); for (int u,v;e--;) { scanf("%d%d",&u,&v); ++d1[u],++d2[v]; ++edg[u][v]; row[u]^=1LL<<v; col[v]^=1LL<<u; } for (int i=1;i<=n;i++) if (i!=v) add(row[i]); if (add(row[v])) win(); else lost(); }