1. 程式人生 > 實用技巧 >題解 SP11414 【COT3 - Combat on a tree】

題解 SP11414 【COT3 - Combat on a tree】

發現本題為公平組合遊戲,因此考慮用 \(\text{SG}\) 函式來解決。

\(\text{SG}(x)\) 為以 \(x\) 為根的子樹的 \(\text{SG}\) 值。對於點 \(x\),可以列舉其子樹內的每一個白點,刪去該點到 \(x\) 的所有點,然後以 \(x\) 為根的子樹會分裂為一個森林,也就是 \(x\) 狀態可以轉移到這個森林的狀態,一個森林的 \(\text{SG}\) 值為每棵樹的 \(\text{SG}\) 值異或和。\(\text{SG}(x)\) 即為子樹內所有白點刪去後形成的森林對應的 \(\text{SG}\) 值取 \(\text{mex}\)

考慮用資料結構來優化這個過程,每個節點上都用一個 \(01\ Trie\)

來維護其子樹內所有白點刪去後對應的森林的 \(\text{SG}\) 值。設 \(y\)\(x\) 的一個兒子,那麼 \(y\) 對應的 \(01\ Trie\) 中維護的資訊還需再異或上 \(x\) 其他兒子的 \(\text{SG}\) 值的異或和才是其正確的資訊。整體異或一個值可以用打標記,交換左右兒子來實現。加上 \(y\) 的貢獻即為將 \(y\) 對應的 \(01\ Trie\) 合併到 \(x\) 上。

最後再 \(dfs\) 一遍,若一個點的後繼狀態為必敗狀態,則該點可以作為第一步選的點。

#include<bits/stdc++.h>
#define maxn 200010
#define maxm 4000010
using namespace std;
template<typename T> inline void read(T &x)
{
    x=0;char c=getchar();bool flag=false;
    while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
    while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
    if(flag)x=-x;
}
int n,tot,cnt;
int c[maxn],sg[maxn],ans[maxn],rt[maxn],ch[maxm][2],tag[maxm];
bool cov[maxm];
struct edge
{
    int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
    e[++edge_cnt]={to,head[from]},head[from]=edge_cnt;
}
void pushtag(int p,int k,int x)
{
    if(k==-1) return;
    if(x>>k&1) swap(ch[p][0],ch[p][1]);
    tag[p]^=x;
}
void pushdown(int p,int k)
{
    if(!tag[p]) return;
    pushtag(ch[p][0],k-1,tag[p]),pushtag(ch[p][1],k-1,tag[p]),tag[p]=0;
}
void insert(int &p,int k,int x)
{
    if(!p) p=++tot;
    if(k==-1)
    {
        cov[p]=true;
        return;
    }
    insert(ch[p][x>>k&1],k-1,x);
    cov[p]=cov[ch[p][0]]&cov[ch[p][1]];
}
int merge(int x,int y,int k)
{
    if(!x||!y) return x+y;
    if(k==-1)
    {
        cov[x]|=cov[y];
        return x;
    }
    pushdown(x,k),pushdown(y,k);
    ch[x][0]=merge(ch[x][0],ch[y][0],k-1);
    ch[x][1]=merge(ch[x][1],ch[y][1],k-1);
    cov[x]=cov[ch[x][0]]&cov[ch[x][1]];
    return x;
}
int mex(int p,int k)
{
    if(!p||k==-1) return 0;
    pushdown(p,k);
    if(cov[ch[p][0]]) return (1<<k)|mex(ch[p][1],k-1);
    return mex(ch[p][0],k-1);
}
void dfs_get(int x,int fa)
{
    int v=0;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(y==fa) continue;
        dfs_get(y,x),v^=sg[y];
    }
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(y==fa) continue;
        pushtag(rt[y],20,v^sg[y]);
        rt[x]=merge(rt[x],rt[y],20);
    }
    if(!c[x]) insert(rt[x],20,v);
    sg[x]=mex(rt[x],20);
}
void dfs_ans(int x,int fa,int v)
{
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(y==fa) continue;
        v^=sg[y];
    }
    if(!v&&!c[x]) ans[++cnt]=x;
    for(int i=head[x];i;i=e[i].nxt)
    {
        int y=e[i].to;
        if(y==fa) continue;
        dfs_ans(y,x,v^sg[y]);
    }
}
int main()
{
    read(n);
    for(int i=1;i<=n;++i) read(c[i]);
    for(int i=1;i<n;++i)
    {
        int x,y;
        read(x),read(y);
        add(x,y),add(y,x);
    }
    dfs_get(1,0);
    if(!sg[1]) puts("-1");
    else
    {
        dfs_ans(1,0,0),sort(ans+1,ans+cnt+1);
        for(int i=1;i<=cnt;++i) printf("%d ",ans[i]);
    }
    return 0;
}