JZOJ4843. 【GDOI2017模拟11.2】相位幻击

题目描述

这里写图片描述

分析

我们知道xor是可逆的,那么对于一个询问x,y,设lca是z,假如我们设val[i]为i到根的xor值,就可以知道val[x]^val[y]^val[father(z)]是答案嘛。
另外,xor题一般的思路是先拆位。
那么对于某个节点我们要让他整颗子树原本的0,1状态掉转(xor 1),或者不动(xor 0)。现在考虑如何放到val上来:我们发现它子树的点跟他深度奇偶性相同的点val是不变的,而奇偶性不同的都翻转了。那这拿线段树以dfn为下标就很好维护了。

代码

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
const int N=200005;
struct rec
{
    int tag;
    //int v;
};
char ch;
int b[N*2],next[N*2],first[N],tt;
int tr[2][N*4],fa[N][18],f[2][N],dis[N],n,m,a[N],st[N],en[N],t[2],x,y,i,j,l,dur,lc;
void cr(int x,int y)
{
    tt++;
    b[tt]=y;
    next[tt]=first[x];
    first[x]=tt;
}
void swap(int &x,int &y)
{
    int z=x;
    x=y;
    y=z;
}
void down(int ty,int x,int l,int r)
{
    if (l!=r)
    {
        tr[ty][x*2]^=tr[ty][x];
        tr[ty][x*2+1]^=tr[ty][x];
        tr[ty][x]=0;
    }
}
void change(int ty,int x,int l,int r,int i,int j)
{
    int m=(l+r)/2;
    down(ty,x,l,r);
    if (l==i&&r==j)
    {
        tr[ty][x]^=y;
        return;
    }
    if (m>=j) change(ty,x*2,l,m,i,j);else
    if (m<i) change(ty,x*2+1,m+1,r,i,j);else
    {
        change(ty,x*2,l,m,i,m);
        change(ty,x*2+1,m+1,r,m+1,j);
    }
}
int get(int ty,int x,int l,int r,int pos)
{
    int m=(l+r)/2;
    down(ty,x,l,r);
    if (l==r)
        return f[ty][l]^tr[ty][x];
    if (m>=pos) 
        return get(ty,x*2,l,m,pos);
    else 
        return get(ty,x*2+1,m+1,r,pos);
}
int lca(int x,int y)
{
    if (dis[x]<dis[y]) swap(x,y);
    int i;
    if (dis[x]!=dis[y])
    {
        i=trunc(log(double(dis[x]-dis[y]))/log(2));
        while (i>=0)
        {
            while (i>=0&&dis[fa[x][i]]<dis[y]) i--;
            if (i==-1) break;
            x=fa[x][i];
            i--;
        }
    }
    if (x==y) return x;
    i=trunc(log(double(dis[x]))/log(2));
    while (i>=0)
    {
        while (i>=0&&fa[x][i]==fa[y][i]) i--;
        if (i==-1) break;
        x=fa[x][i];
        y=fa[y][i];
    }
    return fa[x][0];
}
void dfs(int x,int y)
{
    a[x]^=a[y];
    fa[x][0]=y;
    dis[x]=dis[y]+1;
    int tmp=dis[x]%2;
    t[tmp]++;
    f[tmp][t[tmp]]=a[x];
    st[x]=t[tmp];
    for(int p=first[x];p;p=next[p])
        if (b[p]!=y)
            dfs(b[p],x);
    en[x]=t[tmp];
}
int F(int x)
{
    if (!x) return 0;
    return get(dis[x]%2,1,1,t[dis[x]%2],st[x]);
}
int main()
{
    freopen("phase.in","r",stdin);
    freopen("phase.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n-1)
    {
        scanf("%d%d",&x,&y);
        cr(x,y);
        cr(y,x);
    }
    fo(i,1,n) scanf("%d",a+i);
    dfs(1,0);
    fo(j,1,trunc(log((double)n)/log(2)))
        fo(i,1,n)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    scanf("%d\n",&m);
    fo(l,1,m)
    {
        scanf("%c %d %d\n",&ch,&x,&y);
        if (ch=='C')
            change(dis[x]%2,1,1,t[dis[x]%2],st[x],en[x]);
        else
        {
            lc=lca(x,y);
            dur=F(x)^
                F(y)^
                F(lc)^
                F(fa[lc][0]);
            printf("%d\n",dur);
        }
    }
}

思考

虽然一般来说,直接维护某个点到根的路径的信息大部分时候不优,但也有特殊情况,这道题就是一例,做题时,我们不能轻易略过某种思路,必须思考一下,也许会有转机。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值