SPOJ COT3 - Combat on a tree

空気力学の詩發表於2024-08-07

挺好的一個題,算是博弈和 DS 的有機結合

這類問題一眼考慮 SG 函式,同時樹上的 SG 函式一般都是從子樹向上遞推

考慮若某個點的子樹內全是黑點,則其 SG 函式為零;否則考慮列舉所有的後繼狀態

不難發現選中一個白點會把這個子樹斷成一個森林,這個後繼狀態的 SG 函式就是每個連通塊 SG 函式的異或值

因此可以想要用某種資料結構維護在每個子樹內刪去一個白點得到的後繼狀態的 SG 函式值

需要支援的操作有:合併(用來將子樹資訊向上傳遞);修改(合併多個子樹時需要整體異或某個值);查詢 mex

用 0/1Trie 即可實現上述所有功能,其中修改可以用類似線段樹懶標記的方法,查詢 mex 時只需要維護每個子樹是否是滿二叉樹,類似線段樹上二分地遞迴查詢即可

總複雜度 \(O(n\log n)\)

#include<cstdio>
#include<iostream>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,c[N],sg[N],rt[N],x,y; vector <int> v[N],ans;
class Trie
{
    private:
        int ch[N*20][2],full[N*20],tag[N*20],idx;
        inline void pushup(CI now)
        {
            full[now]=full[ch[now][0]]&full[ch[now][1]];
        }
        inline void pushdown(CI now,CI k)
        {
            if (tag[now]) update(ch[now][0],tag[now],k-1),update(ch[now][1],tag[now],k-1),tag[now]=0;
        }
    public:
        inline void insert(int& now,CI x,CI k=19)
        {
            if (!now) now=++idx;
            if (k==-1) return (void)(full[now]=1);
            pushdown(now,k);
            insert(ch[now][(x>>k)&1],x,k-1);
            pushup(now);
        }
        inline void update(CI now,CI mv,CI k=19)
        {
            if (!now||k==-1) return;
            if ((mv>>k)&1) swap(ch[now][0],ch[now][1]);
            tag[now]^=mv;
        }
        inline int merge(CI x,CI y,CI k=19)
        {
            if (!x||!y) return x|y;
            if (k==-1) return full[x]|=full[y],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);
            pushup(x); return x;
        }
        inline int getmex(CI now,CI k=19)
        {
            if (!now||k==-1) return 0;
            pushdown(now,k);
            if (full[ch[now][0]]) return (1<<k)|getmex(ch[now][1],k-1);
            return getmex(ch[now][0],k-1);
        }
}T;
inline void DFS1(CI now=1,CI fa=0)
{
    int ret=0; for (auto to:v[now])
    if (to!=fa) DFS1(to,now),ret^=sg[to];
    for (auto to:v[now]) if (to!=fa)
    T.update(rt[to],ret^sg[to]),rt[now]=T.merge(rt[now],rt[to]);
    if (!c[now]) T.insert(rt[now],ret);
    sg[now]=T.getmex(rt[now]);
    //printf("sg[%d] = %d\n",now,sg[now]);
}
inline void DFS2(CI now=1,CI fa=0,CI pre=0)
{
    int ret=0; for (auto to:v[now]) if (to!=fa) ret^=sg[to];
    if ((ret^pre)==0&&!c[now]) ans.push_back(now);
    for (auto to:v[now]) if (to!=fa) DFS2(to,now,pre^(ret^sg[to]));
}
int main()
{
    scanf("%d",&n); for (RI i=1;i<=n;++i) scanf("%d",&c[i]);
    for (RI i=1;i<n;++i) scanf("%d%d",&x,&y),v[x].push_back(y),v[y].push_back(x);
    if (DFS1(),sg[1]==0) return puts("-1"),0;
    DFS2(); sort(ans.begin(),ans.end());
    for (auto x:ans) printf("%d\n",x);
    return 0;
}