【HDU-5963】朋友【树上博弈】

题意:

      给定一棵树,树中每一条边有一个权值为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;
}

猜你喜欢

转载自blog.csdn.net/qq_41552508/article/details/82024263