[BZOJ 2819]nim(DFS序+树状数组+SG博弈)

题目链接

http://www.lydsy.com/JudgeOnline/problem.php?id=2819

思路

其实就是要支持查询树上一段路径的xor和,并支持修改点权。。。
如果我在考场上的话绝对上树剖或者LCT。。。

但是此题n实在太大了,会卡常数,而且丧病的是vfk构造的最后3个点全部是链,直接把DFS卡爆栈。。。

正确的做法是,由于树的形态不变,所以我们首先通过BFS或者手写一个栈来模拟DFS过程,生成这棵树的DFS序,不妨设点 x 在DFS序中的位置为 pos[x] 。然后注意到两个点 a,b pos[a] pos[b] 的xor前缀和就是 a b 的路径上不包含 lca(a,b) 这个点以外其他点的xor点权和。因此我们可以维护这个DFS序的一个树状数组,这个BIT中保存的是亦或和前缀。然后查询操作就很好求了。

修改操作也很简单,假如当前要修改的点是 x ,因为 x 的点权只会影响到它的子树里的点的亦或和前缀,那么相当于要在 x 的子树里修改 x 的点权,这个在BIT里也很好实现。

然后注意BIT的下标不能为0,因此所有的 pos 数组都要加1,防止出现下标为0

代码

最近脑洞太大了。。。居然刚开始把LCA放到了if外头,贡献RE十几发!!坑爹啊。。。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define lowbit(x) ((x)&(-(x)))
#define MAXN 500005

using namespace std;

int n;

struct edge
{
    int u,v,next;
}edges[MAXN*2];

int head[MAXN],nCount=0;

void AddEdge(int U,int V)
{
    edges[++nCount].u=U;
    edges[nCount].v=V;
    edges[nCount].next=head[U];
    head[U]=nCount;
}

int fa[MAXN][20],depth[MAXN];
int len=0; //pos[i]=点i在DFS序中的位置
int L[MAXN],R[MAXN];

void DFS(int u) //以u为起点dfs
{
    L[u]=++len;
    for(int p=head[u];p!=-1;p=edges[p].next)
    {
        int v=edges[p].v;
        if(v==fa[u][0]) continue;
        fa[v][0]=u;
        depth[v]=depth[u]+1;
        DFS(v);
    }
    R[u]=len;
}

void prework()
{
    for(int i=1;i<=18;i++)
        for(int j=1;j<=n;j++)
            fa[j][i]=fa[fa[j][i-1]][i-1];
}

int LCA(int a,int b)
{
    if(depth[a]<depth[b]) swap(a,b); //保证a比b深度大
    for(int i=18;i>=0;i--)
    {
        //if(!fa[a][i]) continue;
        if(depth[fa[a][i]]<=depth[b]) continue;
        a=fa[a][i];
    }
    if(depth[a]!=depth[b]) a=fa[a][0];
    if(a==b) return a;
    for(int i=18;i>=0;i--)
    {
        if(!fa[a][i]||!fa[b][i]) continue;
        if(fa[a][i]==fa[b][i]) continue;
        a=fa[a][i],b=fa[b][i];
    }
    a=fa[a][0],b=fa[b][0];
    return a;
}
int bit[MAXN];

void update(int x,int val)
{
    while(x<=n)
    {
        bit[x]^=val;
        x+=lowbit(x);
    }
}

int query(int x)
{
    int ans=0;
    while(x>0)
    {
        ans^=bit[x];
        x-=lowbit(x);
    }
    return ans;
}

int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='0')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}

int num[MAXN];

int main()
{
    memset(head,-1,sizeof(head));
    //scanf("%d",&n);
    n=read();
    for(int i=1;i<=n;i++) //scanf("%d",&num[i]);
        num[i]=read();
    for(int i=1;i<n;i++)
    {
        int u,v;
        //scanf("%d%d",&u,&v);
        u=read(),v=read();
        AddEdge(u,v);
        AddEdge(v,u);
    }
    DFS(1);
    prework();
    for(int i=1;i<=n;i++)
        update(L[i],num[i]),update(R[i]+1,num[i]);
    int q;
    q=read();
    //scanf("%d",&q);
    while(q--)
    {
        char cmd[2];
        int x,y;
        scanf("%s",cmd);
        //scanf("%d%d",&x,&y);
        x=read(),y=read();
        if(cmd[0]=='Q')
        {
            int lca=LCA(x,y); //!!!!!
            int ans=query(L[x])^query(L[y])^num[lca];
            if(ans) puts("Yes");
            else puts("No");
        }
        else
        {
            update(L[x],num[x]),update(R[x]+1,num[x]);
            num[x]=y;
            update(L[x],y),update(R[x]+1,y);
        }
    }
    return 0;
}

你可能感兴趣的:([BZOJ 2819]nim(DFS序+树状数组+SG博弈))