HDU5963-朋友

题目描述

  B君在围观一群男生和一群女生玩游戏,具体来说游戏是这样的:
给出一棵n个节点的树,这棵树的每条边有一个权值,这个权值只可能是0或1。 在一局游戏开始时,会确定一个节点作为根。接下来从女生开始,双方轮流进行 操作。 当一方操作时,他们需要先选择一个不为根的点,满足该点到其父亲的边权为1; 然后找出这个点到根节点的简单路径,将路径上所有边的权值翻转(即0变成1,1 变成0 )。 当一方无法操作时(即所有边的边权均为0),另一方就获得了胜利。
如果在双方均采用最优策略的情况下,女生会获胜,则输出“Girls win!”,否则输 出“Boys win!”。
为了让游戏更有趣味性,在每局之间可能会有修改边权的操作,而且每局游戏指 定的根节点也可能是不同的。
具体来说,修改边权和进行游戏的操作一共有m个,具体如下:
“0 x”表示询问对于当前的树,如果以x为根节点开始游戏,哪方会获得胜利。
“1 x y z ”表示将x和y之间的边的边权修改为z。
B君当然知道怎么做啦!但是他想考考你。

Input

包含至多5组测试数据。
第一行有一个正整数,表示数据的组数。
接下来每组数据第一行,有二个空格隔开的正整数n,m,分别表示点的个数,操 作个数。保证n,m< 40000。
接下来n-1行,每行三个整数x,y,z,表示树的一条边。保证1<x<n, 1<y< n, 0 <= z <= 1。
接下来m行,每行一个操作,含义如前所述。保证一定只会出现前文中提到的两 种格式。
对于操作0,保证1 <= x <= n ;对于操作1,保证1 <= x <= n, 1 <= y <= n, 0 <= z <= 1,保证树上存在一条边连接x和y。

Output

对于每组数据的每一个询问操作,输出一行“Boys win!”或者“Girls win!”。

Sample Input

2
2 3
1 2 0
0 1
1 2 1 1
0 2
4 11
1 2 1
2 3 1
3 4 0
0 1
0 2
0 3
0 4
1 2 1 0
0 1
0 2
0 3
1 3 4 1
0 3
0 4
Sample Output
Boys win!
Girls win!
Girls win!
Boys win!
Girls win!
Boys win!
Boys win!
Girls win!
Girls win!
Boys win!
Girls win! 
思路分析:
  关于这道题我们可以考虑,设如果当前要反转的节点不是与当前游戏的根相连的话,在一方操作完之后,该节点上方所有的边的边权都会
发生翻转,例如下图所示,我们设3-7初始边权为1,0-3为0,在翻转完7号节点之后,0-3之间的边权变为了1,则之后后手可以继续翻转3
号节点以复原除3-7外其他被修改的边权,这就相当于我们以把一条边权为1的边变为0为代价将游戏的当前操作方又变回了原来的先手。那么
若这么考虑下去,先手必输。

但我们还需要考虑,后手要翻转边是有条件的,及该边与其父节点的边权为1,即1.有父节点,2.边权为1.
在先手翻完一条边后,后手应该从先手所翻转的点向上找到第一个可以翻转的点进行复原,但如果上面的点是根节点的话,后手将无法操作,
只好从一条新的树边开始,那么此时先手边掌握了主动权,直到再次遇到一条与根相连边权为1的点为止。如果主动权变换次数为偶数了话,
最终还是后手,即男孩赢,反之女孩赢。那么对于每个节点,只需统计与他相连的且边权为1的节点个数,当为偶数时,boy win,反之,
girl win。最后注意修改即可。
附上代码
 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 const int N=1e6+10;
 5 int Head[N],tot,sum[N];
 6 struct Node{
 7     int next,to,dis;
 8 }edge[N]; 
 9 void Add(int x,int y,int z){
10     edge[++tot].to=y;
11     edge[tot].next=Head[x];
12     edge[tot].dis=z;
13     Head[x]=tot;
14 }
15 void update(int x,int y,int z){
16     for(int i=Head[x];i;i=edge[i].next){
17         int v=edge[i].to;
18         if(v==y){
19             if(edge[i].dis==z) return;  //如果修改值相同就不用改了. 
20             else if(z){
21                 sum[x]++;
22                 edge[i].dis++;
23             }
24             else{
25                 sum[x]--;
26                 edge[i].dis--;
27             }
28             return;
29         }
30     }
31 }
32 void work(){
33     int x;
34     scanf("%d",&x);
35     if(sum[x]%2==0) printf("Boys win!
");
36     else printf("Girls win!
");
37 }
38 int main(){
39     int T;
40     scanf("%d",&T);
41     while(T--){
42         memset(Head,0,sizeof(Head)); //多组数据需要更新。 
43         memset(edge,0,sizeof(edge));
44         memset(sum,0,sizeof(sum));
45         tot=0;
46         int n,m;
47         scanf("%d%d",&n,&m);
48         for(int i=1;i<n;++i){
49             int x,y,z;
50             scanf("%d%d%d",&x,&y,&z);
51             Add(x,y,z);Add(y,x,z);  //加边建树 
52             sum[x]+=z;sum[y]+=z;   //统计相连且边权为1个数 
53         }
54         for(int i=1;i<=m;++i){
55             int t;
56             scanf("%d",&t);
57             if(t){
58                 int x,y,z;
59                 scanf("%d%d%d",&x,&y,&z);
60                 update(x,y,z);update(y,x,z);  //进行更新 
61             }
62             else work();
63         }
64     }
65     return 0;
66 }
View Code



原文地址:https://www.cnblogs.com/li-jia-hao/p/12690444.html