[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;
}