zoukankan      html  css  js  c++  java
  • SP11414 COT3

    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;
    }
    
  • 相关阅读:
    Git的分支的clone、提交及删除
    Linux查找整个目录下包含关键词的文件并全局替换文件内容
    解决Mysql group_concat长度限制
    SQLSERVER建立MYSQL连接服务器
    批处理创建文件夹
    表分区常用脚本
    添加别名的重要性
    floor相关
    T-SQL 小数点转换百分数
    开启MSDTC
  • 原文地址:https://www.cnblogs.com/sdlang/p/13630912.html
Copyright © 2011-2022 走看看