C - 朋友 HDU - 5963
链接:https://vjudge.net/contest/400079#problem/C
思路:
以某个结点作为根进行博弈的时候,无论翻转哪个结点,与根结点相连的边值是一定会翻转。所以只要考虑与根相连的结点话,两人的最优策略就出来了:若对方翻转后使一个与根结点相连的结点p与其之间边L的权值变为1,那自己就翻转以p为祖先的结点使得该条边L变回原状态(一定有这样的点,最差的情况就是翻转p)。对于与根相连的边来说,每次对手这样翻转你就一定能还原对方的操作。若对方翻转后使一个与根结点相连的结点p与其之间边L的权值变为0,或你不能再对L操作,或你也可以模仿对方的操作,但就如上所说,对方一定能还原你的操作。因此操作翻转了初始状态就为1的与跟相连的边L的操作最终不会被还原,是有效的,翻转初始状态为0的边L是无效的。所以初始与根相连的边权值为1的边的个数决定了输赢。若为偶数个,则后手胜,奇数个则先手胜。
因此只要统计与每个点连接的边的个数的奇偶性就能得出胜负。
代码如下;
1 #include <iostream> 2 #include <cmath> 3 #include <algorithm> 4 #include <cstring> 5 #include <queue> 6 #include <cstdio> 7 #include <map> 8 using namespace std; 9 #define ll long long int 10 const int maxn=1e5; 11 int nod[40005];//nod[i]记录与点i相连的权值为1的边的数量 12 map<int,int> tree; 13 int main() 14 { 15 int T; 16 int n,m; 17 scanf("%d",&T); 18 while(T--) 19 { 20 tree.clear(); 21 memset(nod,0,sizeof(nod)); 22 scanf("%d%d",&n,&m); 23 for(int i=1;i<n;i++){ 24 int x,y,z; 25 scanf("%d%d%d",&x,&y,&z); 26 if(z==1){ 27 nod[x]++; 28 nod[y]++; 29 tree[x*maxn+y]=1;//记录下两个点之间边的值 30 tree[y*maxn+x]=1; 31 } 32 } 33 while(m--) 34 { 35 int k; 36 scanf("%d",&k); 37 if(k==1){ 38 int x,y,z; 39 scanf("%d%d%d",&x,&y,&z); 40 if(z==1&&tree[x*maxn+y]==0)//如果原来两个点之间的边的值为0 41 { 42 nod[x]++; 43 nod[y]++; 44 tree[x*maxn+y]=tree[y*maxn+x]=1; 45 46 } 47 if(z==0&&tree[x*maxn+y]==1)//如果原来两个点之间的边的值为1 48 { 49 nod[x]--; 50 nod[y]--; 51 tree[x*maxn+y]=tree[y*maxn+x]=0; 52 } 53 } 54 else if(k==0) 55 { 56 int x; 57 scanf("%d",&x); 58 if(nod[x]&1) 59 printf("Girls win!\n"); 60 else 61 printf("Boys win!\n"); 62 } 63 } 64 } 65 return 0; 66 }