[HDU - 5963 ]朋友 (树上博弈,思维)
[HDU - 5963 ]朋友 (树上博弈,思维)
题目链接: HDU - 5963
题面:
思路:
我们通过推理应该知道如下性质:
-
对于根节点的每一个子树,操作上互不干扰。而且玩家的任何操作没有技巧性,即两人随便操作不影响最终的赢家。
-
对于根节点每一个边权为1的儿子,想将该整个子树完全变为0,需要做奇数次操作。
那么我们只需要维护出每一个节点边权为1的出边个数即可,偶数个boys 赢,奇数个girls赢。
关于第二个性质的证明:
- 当整个子树为一条链,那么链中连续的0和1可以缩为一个,那么链条一定为以1为起点以1为结尾的交叉序列,显然长度一定为奇数。而玩家每一次翻转操作只会减短一个序列元素,所以需要奇数次操作。
- 当子树中某个节点\(now\)有多个儿子时,先只考虑一个儿子,则为链的情况,需要奇数次操作。根节点到节点\(now\)路径中边都变为0,那么该节点的其他儿子与根节点形成的链都是为以0为起点以1为结尾的交叉序列,显然长度一定为偶数。最终还是需要奇数次操作。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int maxn=40000+10;
#define mp make_pair
int n,m;
set<pii> st[maxn];
int info[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d %d",&n,&m);
for(int i=2;i<=n;++i)
{
int x,y,z;
scanf("%d %d %d",&x,&y,&z);
st[x].insert(mp(y,z));
st[y].insert(mp(x,z));
if(!z)
continue;
info[x]++;
info[y]++;
}
while(m--)
{
int op,x,y,z;
scanf("%d",&op);
if(op==0)
{
scanf("%d",&x);
int ans=info[x];
if(ans&1)
{
puts("Girls win!");
}else
{
puts("Boys win!");
}
}else
{
scanf("%d %d %d",&x,&y,&z);
if(st[x].count(mp(y,z))==1)
{
continue;
}else
{
if(z==0)
{
info[x]--;info[y]--;
}else
{
info[x]++;info[y]++;
}
st[x].erase(st[x].lower_bound(mp(y,!z)));
st[y].erase(st[y].lower_bound(mp(x,!z)));
st[x].insert(mp(y,z));
st[y].insert(mp(x,z));
}
}
}
for(int i=1;i<=n;++i)
{
st[i].clear();
info[i]=0;
}
}
return 0;
}
本博客为本人原创,如需转载,请必须声明博客的源地址。
本人博客地址为:www.cnblogs.com/qieqiemin/
希望所写的文章对您有帮助。