没有传送门,题解原始idea来自下面这篇博文。
1-1 T2 rab(博弈论)
题意
给定一棵树,初始时非叶节点均为无色,叶节点会是红色、蓝色或无色。
小红和小蓝轮流给无色叶子染色(小红染红色,小蓝染蓝色,小红先染)。所有叶子染完后,非叶节点的颜色将被逐一确定:一个非叶节点的颜色是它所有儿子的颜色中出现较多的那个(保证有奇数个儿子)。最后,根是谁的颜色谁就获胜。
求小红是否能赢,若能赢,求出第一步选择哪些叶子能赢。
数据范围
对于20%的数据,t=1,n≤20。
对于60%的数据,n≤2000。
对于100%的数据,t≤10,n≤100000。
题解
看的是如上大佬的题解,已经讲的很清楚了。
我们现根据已染色点dfs推断出所有颜色一定了的节点。
若此时已经可以得出根节点的颜色,那么小红第一次染色就随便染,即任意一个未被染色的叶节点。
若此时还并能确定,那么为了保证小红的最终胜利,一定要占领先机,即先把有儿子未染色且这个非叶节点颜色也不确定的染了(保证这个节点为红),接着把蓝儿子比红儿子多一的染了(因为如果多于了一,染它的儿子显然是无用的,这里其实并不是很显然,因为我思考的时候就是在这里卡了)。
补充一下:
关于博弈论,由最后一步必胜不断递归转移。所以对上一段操作可以这样理解:当我们把蓝儿子多一的染成无色后,小蓝染色时,无论他选择新的节点染色还是就在这个节点上又恢复蓝儿子多一的状态,对于小红都是可以在这个基础上又新占领一个非叶节点的。而这样每轮两人各加一,先手必胜,则小红赢。
分析的时候,假设对于某非叶节点,若为无色(或红儿子数等于蓝儿子数)并还有儿子未被染色,由于小红先行的规则,则可以保证染了一个叶节点就可以使这个节点为红。
具体的看代码的实现吧。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=1e5+10;
int T,n,cnt,col[N],dyed[N],num[N];
int head[N],to[N],nxt[N],tot;
int ans[N];
bool leaf[N],vis[N];
inline void link(int u,int v)
{
to[++tot]=v;nxt[tot]=head[u];head[u]=tot;
}
inline void df(int k)
{
if(leaf[k]) return;
int nnum=0;
for(int i=head[k];i!=-1;i=nxt[i]){
df(to[i]);
if(col[to[i]]==1) nnum--;
else if(col[to[i]]==0) nnum++;
}
num[k]=nnum;
if(nnum>0) col[k]=0;
else if(nnum==0) col[k]=-1;
else col[k]=1;
}
// 本来想在读入col[i]时判断非叶节点颜色
// 然后发现还是只能dfs实现
inline void dfs(int k)
{
if(leaf[k]){
if(col[k]==-1) ans[++cnt]=k;
return;
}
for(int i=head[k];i!=-1;i=nxt[i]){
if(col[to[i]]==-1) dfs(to[i]);
else if(col[to[i]]==1 && num[to[i]]==-1) dfs(to[i]);
}
}
int main(){
scanf("%d",&T);
while(T--){
cnt=0;tot=0;
memset(head,-1,sizeof(head));
memset(leaf,true,sizeof(leaf));
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
if(x!=0) link(x,i),leaf[x]=false;
}
for(int i=1;i<=n;i++){
scanf("%d",&col[i]);
}
df(1);
if(col[1]==0){
for(int i=1;i<=n;i++){
if(leaf[i] && col[i]==-1){
ans[++cnt]=i;
}
}
printf("%d ",cnt);
for(int i=1;i<cnt;i++) printf("%d ",ans[i]);
printf("%d\n",ans[cnt]);
}else if(col[1]==-1){
dfs(1);
printf("%d ",cnt);
sort(ans+1,ans+cnt+1);
for(int i=1;i<cnt;i++) printf("%d ",ans[i]);
printf("%d\n",ans[cnt]);
}
else printf("-1\n");
}
return 0;
}