题意为给出一棵n个节点的树,这棵树的每条边有一个权值,这个权值只可能是0或1。 在一局游戏开始时,会确定一个节点作为根。
当一方操作时,他们需要先选择一个不为根的点,满足该点到其父亲的边权为1; 然后找出这个点到根节点的简单路径,将路径上所有边的权值翻转(即0变成1,1 变成0 )。
当一方无法操作时(即所有边的边权均为0),另一方就获得了胜利。
为了让游戏更有趣味性,在每局之间可能会有修改边权的操作,而且每局游戏指 定的根节点也可能是不同的。
具体来说,修改边权和进行游戏的操作一共有m个,具体如下:
- 0 x 表示询问对于当前的树,如果以x为根节点开始游戏,哪方会获得胜利.
- 1 x y z 表示将x和y之间的边权修改为z。
思路是考虑所有和根节点相连的边。若某条边为0,则先手将这条边变为1后,后手总能再将这条边变为0。
所以游戏只和根节点相连的为1的边的个数有关系。
代码如下:
1 #include"cstdio" 2 #include"iostream" 3 #include"cstring" 4 #include"algorithm" 5 #include"cstdlib" 6 #include"vector" 7 #include"set" 8 using namespace std; 9 typedef long long LL; 10 const int MAXN=40010; 11 12 int edgesum[MAXN]; 13 set<int> G[MAXN]; 14 int main() 15 { 16 #ifdef LOCAL 17 freopen("in.txt","r",stdin); 18 //freopen("out.txt","w",stdout); 19 #endif 20 int t; 21 scanf("%d",&t); 22 for(int tt=1;tt<=t;tt++) 23 { 24 memset(edgesum,0,sizeof(edgesum)); 25 int n,m; 26 scanf("%d%d",&n,&m); 27 for(int i=1;i<=n;i++) 28 G[i].clear(); 29 for(int i=1;i<=n-1;i++) 30 { 31 int x,y,z; 32 scanf("%d%d%d",&x,&y,&z); 33 if(z) 34 { 35 G[x].insert(y); 36 G[y].insert(x); 37 edgesum[x]++; 38 edgesum[y]++; 39 } 40 } 41 for(int i=1;i<=m;i++) 42 { 43 int op; 44 int x,y,z; 45 scanf("%d",&op); 46 if(op) 47 { 48 scanf("%d%d%d",&x,&y,&z); 49 if(z==1 && G[x].count(y)==0) 50 { 51 G[x].insert(y); 52 G[y].insert(x); 53 edgesum[x]++; 54 edgesum[y]++; 55 } 56 if(z==0 && G[x].count(y)!=0) 57 { 58 G[x].erase(y); 59 G[y].erase(x); 60 edgesum[x]--; 61 edgesum[y]--; 62 } 63 } 64 if(!op) 65 { 66 int x; 67 scanf("%d",&x); 68 if(edgesum[x]%2) printf("Girls win! "); 69 else printf("Boys win! "); 70 } 71 } 72 } 73 return 0; 74 }