bzoj 4134: ljw和lzr的hack比赛 sg函数+字典树

Description

分曹射覆蜡灯红,膜拜神犇lzr;
渚清沙白鸟飞回,长跪巨神ljw。
lzr就是被称为hack狂魔的qmqmqm,相比很多人都已经知道了。
ljw虽然没有lzr有名,但是在cf、bc等比赛里的hack次数也是数一数二的。
SD的这两位神犇今天决定进行一场hack比赛。
经过研究,他们决定hack SD的另一位神犇jzh做过的题。
我们设jzh已经做过了n道题。这些题目因为知识点之间的联系而形成了一棵树结构。1号题目(A+B problem)是这棵树的根。
因为slyz也不乏hack高手,所以jzh做的这n道题有些已经被hack了。
现在ljw先手,两人轮流操作。一次操作为:选择一道没有被hack过的题x,然后将x到1的路径上的所有没有被hack过的题全部hack掉。
无法操作者输。
我们假设ljw和lzr的智商都是无比的高(事实也是如此),都会采取对自己最有利的操作。
ljw当然想赢下这场比赛了!于是他想算一下最终他能否赢下比赛。如果他能赢,他还想知道在保证他赢的情况下第一步他选择哪些题。
这么简单的问题ljw神犇当然会了。不过他想考考你。
Input

第一行一个整数n(n<=100000),代表jzh神犇做过的题数。
第二行n个整数,每个整数为0或1。其中第i个数为0代表第i道题还没有被hack,第i个数为1代表第i道题已经被slyz的神犇hack了。
接下来n-1行描述jzh做过的题目形成的树。每行两个整数u,v代表第u道题和第v道题之间有一条边。
Output

如果ljw不能赢得比赛,那么输出-1。
否则升序输出所有题号x。x满足ljw在保证赢的情况下第一步可以选择x。
Sample Input

8

1 1 0 1 0 0 1 0

1 2

1 3

2 6

3 4

3 5

5 7

7 8
Sample Output

5

HINT

数据范围:1<=u,v<=n<=100000

分析:
昨天选讲是提到了这一题。
这种题肯定想到 s g 函数。
因为一开始有的点已经染色,所以我们新建一个森林,每个点的父亲是该点到根路径上的第一个白点(黑点没影响),正确性显然。
我们设 s g ( i ) 为子树 i s g 值; g ( x ) 为删除根到 x 的路径后,剩余部分 s g 函数的异或值。
那么, s g ( x ) = m e x ( g ( y ) , y s u b t r e e ( x ) )
考虑递推求 s g 值,我们开一个东西把子树 y g 值记录下来,然后当我们枚举到父亲 x 时,显然 y 子树的每个 g 值都异或上了 x 的其他儿子的 s g 值。对于 x 其他儿子也同理。当然还要加上当前点的 g 值,这个显然是所有子树的 s g 值的异或和。然后我们可以对当前点求 m e x ,显然要维护一个数据结构来实现给全部数异或一个数,插入一个数和求 m e x ,显然一个字典树就可以了,打上一个 l a z y 和记录一个 f u l l 表示该儿子是否为满二叉树。然后 t r i e 合并直接向线段树合并一样就可以了。

代码:

/**************************************************************
    Problem: 4134
    User: liangzihao
    Language: C++
    Result: Accepted
    Time:3900 ms
    Memory:92900 kb
****************************************************************/

#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>

const int maxd=30;
const int maxn=1e5+7;

using namespace std;

int op[maxn],sg[maxn],ls[maxn],root[maxn],fa[maxn],bit[35];
int n,x,y,cnt;
int ans[maxn];

struct node{
    int l,r;
    int full,lazy;
    vector <int> a;
}t[maxn*maxd];

struct edge{
    int y,next;
}g[maxn*2];

void add(int x,int y)
{
    g[++cnt]=(edge){y,ls[x]};
    ls[x]=cnt;
}

void dfs(int x,int f,int top)
{
    fa[x]=top;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (f==y) continue;
        if (op[x]) dfs(y,x,top);
              else dfs(y,x,x);
    }
}

void clean(int p,int now)
{
    if (now<0) return;
    if (t[p].lazy)
    {
        if (t[p].l) t[t[p].l].lazy^=t[p].lazy;
        if (t[p].r) t[t[p].r].lazy^=t[p].lazy;
        if (t[p].lazy&bit[now]) swap(t[p].l,t[p].r);
        t[p].lazy=0;
    }
}

void merge(int &p,int q,int now)
{
    clean(p,now); clean(q,now);
    if (!p||!q)
    {
        p=p+q;
        return;
    }
    if (now<0)
    {
        t[p].full=t[p].full|t[q].full;
        for (int i=0;i<t[q].a.size();i++) t[p].a.push_back(t[q].a[i]);
        t[q].a.clear();
        return;
    }
    merge(t[p].l,t[q].l,now-1);
    merge(t[p].r,t[q].r,now-1);
    t[p].full=t[t[p].l].full&t[t[p].r].full;
}

void ins(int &p,int now,int x,int num)
{
    if (!p) p=++cnt;
    clean(p,now);
    if (now<0)
    {
        t[p].a.push_back(num);
        t[p].full=1;
        return;
    }
    if (x&bit[now]) ins(t[p].r,now-1,x,num);
               else ins(t[p].l,now-1,x,num);
    t[p].full=t[t[p].l].full&t[t[p].r].full;
}

int mex(int p,int now)
{
    clean(p,now);
    if (now<0) return 0;
    if (t[t[p].l].full) return bit[now]+mex(t[p].r,now-1);
                   else return mex(t[p].l,now-1);
}

void solve(int x,int f)
{
    int w=0;
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (f==y) continue;
        solve(y,x);
        w^=sg[y];
    }
    for (int i=ls[x];i>0;i=g[i].next)
    {
        int y=g[i].y;
        if (f==y) continue;
        int l=w^sg[y];
        t[root[y]].lazy^=l;
        merge(root[x],root[y],maxd);
    }
    ins(root[x],maxd,w,x);
    sg[x]=mex(root[x],maxd);
}

void find(int p,int now,int x)
{
    clean(p,now);
    if (now<0)
    {
        for (int i=0;i<t[p].a.size();i++) ans[++cnt]=t[p].a[i];
        return;
    }
    if (x&bit[now]) find(t[p].r,now-1,x);
               else find(t[p].l,now-1,x);
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&op[i]);
    for (int i=1;i<=n-1;i++)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }   
    dfs(1,0,0);
    memset(ls,0,sizeof(ls));
    cnt=0;      
    for (int i=1;i<=n;i++)
    {
        if ((fa[i]) && (!op[i])) add(fa[i],i);
    }
    bit[0]=1;
    for (int i=1;i<=maxd;i++) bit[i]=bit[i-1]*2;
    int lim=0;  
    cnt=0;
    for (int i=1;i<=n;i++)
    {
        if ((!fa[i])&&(!op[i]))
        {
            solve(i,0);
            lim^=sg[i];
        }
    }
    if (!lim) printf("-1");
    else
    {
        cnt=0;
        for (int i=1;i<=n;i++)
        {
            if ((!fa[i])&&(!op[i]))
            {
                find(root[i],maxd,lim^sg[i]);
            }
        }
        sort(ans+1,ans+cnt+1);
        for (int i=1;i<=cnt;i++) printf("%d\n",ans[i]);
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81486455
今日推荐