题意:
给定一棵树,树中每一条边有一个权值为0或者1,每次游戏需要找到一个点,满足该点到其父亲的边权为1,然后找到这个点到根节点的简单路径,将路径上所有边的权值翻转。
当一方无法操作时,另一方就获胜。
每次游戏有m个操作,0 x表示指定x为根节点,要求输出谁会赢;1 x y z表示将x和y之间的边修改为z。
思路:
本题乍一看还是有些难度的。但是仔细思考的话,可以找到一个化繁为简的方法。
可以看出一条边权如果为0,那么至少要翻转偶数次才能变为0;如果是1,那么至少要翻转奇数次才能变为0,因此我们可以只考虑根节点的边,不考虑图中其它的边。
对根节点直接导出的边权进行累加,如果和为偶数,那么后手胜,如果为奇数,那么先手胜。
注意:
本题存图方式很多,可以用 map<Point,int>,这种结构体的形式来存,也可以对两个点进行一下下类似哈希的操作。
如 map<long long,int> mp; mp[a*10000+b]表示<a,b>之间的边权,这些都是可以采用的方式。
代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <map>
#define rep(i,a,b) for(int i = a;i <= b;i++)
using namespace std;
const int N = 4*1e4+100;
struct Point{
int x,y;
}tmp;
map<Point,int> mp;
int n,m;
int deg[N];
bool operator < (Point a,Point b)
{
if(a.x != b.x)
return a.x < b.x;
else
return a.y < b.y;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
mp.clear();
scanf("%d%d",&n,&m);
rep(i,1,n) deg[i] = 0;
rep(i,1,n-1)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
if(z == 1){
deg[x]++,deg[y]++;
int a = min(x,y);
int b = max(x,y);
tmp.x = a; tmp.y = b;
mp[tmp] = z;
}
}
rep(i,1,m)
{
int x;
scanf("%d",&x);
if(x == 0){
int y;
scanf("%d",&y);
if(deg[y]%2 == 0) printf("Boys win!\n");
else printf("Girls win!\n");
}
else{
int x1,y1,z1;
scanf("%d %d %d",&x1,&y1,&z1);
int a1 = min(x1,y1);
int a2 = max(x1,y1);
tmp.x = a1; tmp.y = a2;
if(mp[tmp] == 0 && z1 == 1) {mp[tmp] = z1; deg[a1]++; deg[a2]++;}
else if(mp[tmp] == 1 && z1 == 0){
mp[tmp] = 0; deg[a1]--; deg[a2]--;
}
}
}
}
return 0;
}