SP11414 COT3 - Combat on a tree

Alice和Bob在一棵\(n\)个节点的树上玩游戏。每个节点最初都是黑色或白色。
他们轮流执行以下操作:
从当前树中选择一个白色节点\(v\),将路径\((1,v)\)上的所有白色节点都变为黑色.最后操作的玩家获胜.爱丽丝先手。当他们都使用最佳策略时,求是否能够必胜,并求出第一步的方案.

竟然找到了省选模拟赛的题加强版(

对于这道题,我们考虑计算出每个点的这个子树的sg值,从而得到全局的答案。

那么我们可以很容易发现,对于一个点\(u\),如果这个子树的点都是黑点,那么\(sg(u)=0\),必败,否则子树里的白点可以作为\(u\)的后继状态,假设白点为\(v\),那么\(sg(u)=mex(sg'(v))\)\(mex(S)\)为集合\(S\)中未出现的最小自然数。

而之所以不是\(v\)\(sg\)是因为当选了\(v\)这个点之后,会把当前游戏割裂成多个子游戏,也就是将\(v\)\(u\)的路径上的点删掉,得到了若干个子树,所以\(sg'(v)=\oplus_isg(i)\),其中\(i\)为每个子树的根。

得到了sg值之后,如果\(sg(1)=0\),那么无解,否则对每个点还是做上面的过程,如果得到的值为\(0\),那么可以作为答案。

这样子的复杂度是\(O(n^2)\)的,如果写的丑可能会变成\(O(n^3)\)

我们考虑用trie优化这个过程。

子游戏的异或可以看作以\(v\)为根的子树的集合全部异或上一个数,那么我们记录一个标记,每次下放标记就可以。

对于对一个集合取mex,我们从高位往低位贪心,如果左儿子是满二叉树,那么就往右儿子走,否则走左儿子,类似于一个线段树二分。判断是不是满二叉树可以维护标记来完成。

然后还需要支持集合合并和插入一个数,就不用多说了。

这样时间复杂度为\(O(nlogn)\)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
const int N = 1e5;
using namespace std;
int n,c[N + 5],sg[N + 5],edge[N * 2 + 5],nxt[N * 2 + 5],head[N + 5],edge_cnt,ans[N + 5],m,rt[N + 5];
void add_edge(int u,int v)
{
    edge[++edge_cnt] = v;
    nxt[edge_cnt] = head[u];
    head[u] = edge_cnt;
}
struct Trie
{
    int ch[N * 40 + 5][2],cov[N * 40 + 5],node_cnt,tag[N * 40 + 5],dep[N * 40 + 5];
    void pushup(int k)
    {
        cov[k] = cov[ch[k][0]] & cov[ch[k][1]];
    }
    void insert(int &k,int x,int d)
    {
        if (!k)
        {
            k = ++node_cnt;
            dep[k] = d;
        }
        if (d < 0)
        {
            cov[k] = 1;
            return;
        }
        insert(ch[k][x >> d & 1],x,d - 1);
        pushup(k);
    }
    void modify(int k,int x)
    {
        if (dep[k] < 0)
            return;
        if (x >> dep[k] & 1)
            swap(ch[k][0],ch[k][1]);
        tag[k] ^= x;
    }
    void pushdown(int k)
    {
        if (tag[k])
        {
            modify(ch[k][0],tag[k]);
            modify(ch[k][1],tag[k]);
            tag[k] = 0;
        }
    }
    int merge(int x,int y)
    {
        if (!x || !y)
            return x + y;
        if (dep[x] < 0)
        {
            cov[x] |= cov[y];
            return x;
        }
        pushdown(x);
        pushdown(y);
        ch[x][0] = merge(ch[x][0],ch[y][0]);
        ch[x][1] = merge(ch[x][1],ch[y][1]);
        pushup(x);
        return x;
    }
    int mex(int k)
    {
        if (dep[k] < 0)
            return 0;
        if (!k)
            return 0;
        pushdown(k);
        if (cov[ch[k][0]])
            return (1 << dep[k]) | mex(ch[k][1]);
        return mex(ch[k][0]);
    }
}tree;
void dfs(int u,int fa)
{
    int xx = 0;
    for (int i = head[u];i;i = nxt[i])
    {
        int v = edge[i];
        if (v == fa)
            continue;
        dfs(v,u);
        xx ^= sg[v];
    }
    for (int i = head[u];i;i = nxt[i])
    {
        int v = edge[i];
        if (v == fa)
            continue;
        tree.modify(rt[v],xx ^ sg[v]);
        rt[u] = tree.merge(rt[u],rt[v]);
    }
    if (!c[u])
        tree.insert(rt[u],xx,20);
    sg[u] = tree.mex(rt[u]);
}
void dfs2(int u,int fa,int x)
{
    for (int i = head[u];i;i = nxt[i])
    {
        int v = edge[i];
        if (v == fa)
            continue;
        x ^= sg[v];
    }
    if (!c[u] && !x)
        ans[++m] = u;
    for (int i = head[u];i;i = nxt[i])
    {
        int v = edge[i];
        if (v == fa)
            continue;
        dfs2(v,u,x ^ sg[v]);
    }
}
inline int read()
{
	int X(0),w(0);char ch(0);
	while (!isdigit(ch))w|=ch=='-',ch=getchar();
	while (isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
	return w?-X:X;
}
int main()
{
    //freopen("sp11414.in","r",stdin);
    //freopen("a1.out","w",stdout);
    n = read();
    for (int i = 1;i <= n;i++)
        c[i] = read();
    int u,v;
    for (int i = 1;i < n;i++)
    {
        u = read();
        v = read();
        add_edge(u,v);
        add_edge(v,u);
    }
    dfs(1,0);
    if (!sg[1])
        cout<<-1<<endl;
    else
    {
        dfs2(1,0,0);
        sort(ans + 1,ans + m + 1);
        for (int i = 1;i <= m;i++)
            printf("%d ",ans[i]);
    }
    return 0;
}
posted @ 2020-09-08 10:11  eee_hoho  阅读(273)  评论(0编辑  收藏  举报