1. 程式人生 > >1-1 T2 rab(博弈論)

1-1 T2 rab(博弈論)

題目描述

給定一棵樹,初始時非葉節點均為無色,葉節點會是紅色、藍色或無色。
小紅和小藍輪流給無色葉子染色(小紅染紅色,小藍染藍色,小紅先染)。所有 葉子染完後,非葉節點的顏色將被逐一確定:一個非葉節點的顏色是它所有兒子的顏 色中出現較多的那個(保證有奇數個兒子)。最後,根是誰的顏色誰就獲勝。
求小紅是否能贏,若能贏,求出第一步選擇哪些葉子能贏。

輸入輸出格式

輸入格式:

第一行一個整數t表示資料組數。
每組資料第一行一個整數n表示節點數。
第二行n個整數,第i個整數fi表示i的父親,保證f1=0。
第三行 n個整數,第 i 個整數 gi 表示 i 的初始顏色(0 表示紅色,1表示藍 色,-1表示無色)。

輸出格式:

每組資料輸出一行。
若小紅能贏,先輸出一個整數 m表示第一步可以選的葉子數,接下來 m個 整數表示那些葉子的編號,從小到大輸出。
若你只知道小紅能贏,你可以只輸 出一行一個整數0。
否則輸出一個整數-1。

輸入輸出樣例

輸入樣例#1: 複製
2
2
0 1
-1 -1
2
0 1
-1 1
輸出樣例#1: 複製
1 2
-1

說明

對於20%的資料,t=1,n≤20。
對於60%的資料,n≤2000。
對於100%的資料,t<=10,n≤100000。
若你只判斷對了勝負,可以獲得該測試點一半的分數。

這個題還是挺有趣的。
首先經過一些思考,可以發現:
最優方案一定是一開始選擇一個葉子染,並且使得它的父親變成

紅色
那麼我們可以發現,這樣的點滿足兒子中紅色節點數量=藍色節點數量
此時我們可以將它染成無色,恰好滿足題目的意思(通過一步改變它的顏色)
類似的可以染出紅色和藍色,分別代表雙方必勝的情況
先手必勝當且僅當根節點的顏色為紅色或無色
根節點為紅色時,可以隨便選一個葉子,都是符合要求的
根節點為無色時,分兩種情況:
1.我們可以選擇一個無色兒子染
2.我們可以選擇一個藍色兒子染(必須保證不是葉子,且可以通過一步將它染成無色)
然後就統計出來所有點了,最後排序輸出即可。
程式碼:

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring> #include<iostream> #include<algorithm> #define ll long long #define max(a,b) a>b?a:b #define min(a,b) a<b?a:b using namespace std; inline int read(){ int x=0,f=1;char ch=' '; while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); return x*f; } const int N=1e5+5; int T,n,tot,cnt; int head[N],to[N],Next[N]; int col[N],leaf[N],q[N],num[N]; inline void addedge(int x,int y){to[++tot]=y;Next[tot]=head[x];head[x]=tot;} inline void dfs(int x){ if(leaf[x])return; int cntr=0,cntb=0; for(int i=head[x];i;i=Next[i]){ int u=to[i]; dfs(u); if(col[u]==0)cntr++; else if(col[u]==1)cntb++; } num[x]=cntr-cntb; if(num[x]>0)col[x]=0; else if(!num[x])col[x]=-1; else col[x]=1; } inline void dfs2(int x){ if(leaf[x]){ if(col[x]==-1)q[++cnt]=x; return; } for(int i=head[x];i;i=Next[i]){ int u=to[i]; if(col[u]==-1)dfs2(u); else if(col[u]==1&&num[u]==-1)dfs2(u); else if(leaf[u])dfs2(u); } } int main(){ // freopen("./1-1 T2 data/rab3.in","r",stdin); // freopen("rab3.out","w",stdout); T=read(); while(T--){ n=read();tot=0; for(int i=1;i<=n;i++)head[i]=0,leaf[i]=1; for(int i=1;i<=n;i++){ int fa=read(); if(fa)addedge(fa,i); leaf[fa]=0; } for(int i=1;i<=n;i++)col[i]=read(); dfs(1); if(col[1]==0){ cnt=0; for(int i=1;i<=n;i++)if(leaf[i]&&col[i]==-1)cnt++; printf("%d ",cnt); for(int i=1;i<=n;i++)if(leaf[i]&&col[i]==-1)printf("%d ",i); putchar('\n'); } else if(col[1]==-1){ cnt=0; dfs2(1); printf("%d ",cnt); sort(q+1,q+cnt+1); for(int i=1;i<=cnt;i++)printf("%d ",q[i]); putchar('\n'); } else printf("-1\n"); } return 0; }