bzoj 2908. 又是nand(树链剖分+区间NAND+单点修改)

这篇博客介绍了如何使用线段树解决区间NAND操作的问题,通过位运算的独立性,分别维护从左到右和从右到左的区间信息,实现了对每个位的独立更新和查询。同时,结合树链剖分优化,处理了不同方向的区间查询。文章给出了详细的代码实现,包括线段树节点的维护和区间查询、修改操作。
摘要由CSDN通过智能技术生成

首先考虑问题的简化版
存在下面两个操作

  • 询问 [ l , r ] [l,r] [l,r]区间与非的值即 a l NAND a l + 1 NAND … NAND a r a_l \text{NAND} a_{l+1} \text{NAND}\dots \text{NAND} a_r alNANDal+1NANDNANDar
  • 单线修改 p , x p,x p,x a p = x a_p=x ap=x

这是一道去年校赛题最近才发现区间与非的板子题

首先直觉告诉我们要用线段树维护此操作,但是区间与非没有结合律,这样的信息线段树不能直接维护,不过位运算具有独立性,我们可以一位一位去考虑。

考虑用线段树每个节点维护 L [ 0 / 1 ] , R [ 0 / 1 ] \text{L}[0/1],\text{R}[0/1] L[0/1],R[0/1]

L [ 0 ] \text{L}[0] L[0]表示刚开是 0 0 0,然后从左向右经过此区间是最终的数(此节点维护的区间)
L [ 1 ] \text{L}[1] L[1]表示刚开是 1 1 1,然后从左向右经过此区间是最终的数
R [ 0 ] \text{R}[0] R[0]表示刚开是 0 0 0,然后从右向左经过此区间是最终的数
R [ 1 ] \text{R}[1] R[1]表示刚开是 1 1 1,然后从右向左经过此区间是最终的数

然后只需要维护32棵线段树(按位),就可以区间询问了。

2908. 又是nand

而此题就是套了个树链剖分,并且注意询问的时候有的区间是从左向右,有的区间是从右向左(yy一下树剖的样子即可)

#include<bits/stdc++.h>
using namespace std;
using u32=unsigned int;
using pii=pair<int,int>;
constexpr int N=100010;
int n,m,bit;
u32 a[N];
int h[N],e[2*N],ne[2*N],idx;
int sz[N],son[N],fa[N],dep[N];
int dfn[N],top[N],id[N],timestamp;
void add(int a,int b){e[idx]=b,ne[idx]=h[a],h[a]=idx++;}
struct Segment
{
    struct node
    {
        int l,r;
        bool L[2],R[2];
    }tree[N<<2];
    void pushup(int u)
    {
        tree[u].L[0]=tree[u<<1|1].L[tree[u<<1].L[0]];
        tree[u].L[1]=tree[u<<1|1].L[tree[u<<1].L[1]];
        tree[u].R[0]=tree[u<<1].R[tree[u<<1|1].R[0]];
        tree[u].R[1]=tree[u<<1].R[tree[u<<1|1].R[1]];
    }
    void build(int u,int l,int r,int k)
    {
        tree[u].l=l,tree[u].r=r;
        if(l==r) 
        {
            tree[u].L[0]=tree[u].R[0]=1;
            tree[u].L[1]=tree[u].R[1]=!(a[id[l]]>>k&1);
            return;
        }
        int mid=l+r>>1;
        build(u<<1,l,mid,k);build(u<<1|1,mid+1,r,k);
        pushup(u);
    }
    void modify(int u,int pos,bool x)
    {
        if(tree[u].l==tree[u].r)
        {
            tree[u].L[0]=tree[u].R[0]=1;
            tree[u].L[1]=tree[u].R[1]=(!x);
            return;
        }
        int mid=tree[u].l+tree[u].r>>1;
        if(pos<=mid) 
            modify(u<<1,pos,x);
        else
            modify(u<<1|1,pos,x);
        pushup(u);
    }
    bool queryL(int u,int l,int r,bool c)
    {
        if(l<=tree[u].l&&tree[u].r<=r) return tree[u].L[c];
        int mid=tree[u].l+tree[u].r>>1;
        if(r<=mid)
            return queryL(u<<1,l,r,c);
        else if(l>mid) 
            return queryL(u<<1|1,l,r,c);
        else 
            return queryL(u<<1|1,l,r,queryL(u<<1,l,r,c));
        
    }
    bool queryR(int u,int l,int r,bool c)
    {
        if(l<=tree[u].l&&tree[u].r<=r) return tree[u].R[c];
        int mid=tree[u].l+tree[u].r>>1;
        if(r<=mid)
            return queryR(u<<1,l,r,c);
        else if(l>mid) 
            return queryR(u<<1|1,l,r,c);
        else 
            return queryR(u<<1,l,r,queryR(u<<1|1,l,r,c));
    }
}T[33];
//==============================================================
void dfs1(int u)
{
    dep[u]=dep[fa[u]]+1;
    sz[u]=1;
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]) continue;
        fa[v]=u;
        dfs1(v);
        sz[u]+=sz[v];
        if(sz[son[u]]<sz[v]) son[u]=v;
    }
}

void dfs2(int u,int t)
{
    dfn[u]=++timestamp;
    id[timestamp]=u;
    top[u]=t;
    if(son[u]) dfs2(son[u],t);
    for(int i=h[u];i!=-1;i=ne[i])
    {
        int v=e[i];
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}
//==============================================================
void update(int u,u32 x)
{
    for(int k=0;k<bit;k++)
        T[k].modify(1,dfn[u],x>>k&1);
}
u32 ask(int u,int v)
{
    u32 ans=0;
    vector<pii> ql,qr;
    while(top[u]!=top[v])
    {
        if(dep[top[u]]>=dep[top[v]])// debug 1h
        {
            qr.push_back({dfn[top[u]],dfn[u]});
            u=fa[top[u]];
        }
        else
        {
            ql.push_back({dfn[top[v]],dfn[v]});
            v=fa[top[v]];
        }
    }
    if(dep[u]>=dep[v]) 
        qr.push_back({dfn[v],dfn[u]});
    else
        ql.push_back({dfn[u],dfn[v]});
    
    reverse(ql.begin(),ql.end());    
    
    for(pii t:qr)
    {
        int l=t.first,r=t.second;
        for(int k=0;k<bit;k++)
            ans=ans-(ans&(1<<k))+(T[k].queryR(1,l,r,ans>>k&1)<<k);
    }
    for(pii t:ql)
    {
        int l=t.first,r=t.second;
        for(int k=0;k<bit;k++)
            ans=ans-(ans&(1<<k))+(T[k].queryL(1,l,r,ans>>k&1)<<k);
    }
    return ans;
}
int main()
{
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    memset(h,-1,sizeof h);
    cin>>n>>m>>bit;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<n;i++)
    {
        int u,v;
        cin>>u>>v;
        add(u,v),add(v,u);
    }
    dfs1(1);
    dfs2(1,1);
    
    for(int k=0;k<bit;k++)
        T[k].build(1,1,n,k);
        
    while(m--)
    {
        char op[10];u32 a,b;
        cin>>op>>a>>b;
        if(*op=='R')
            update(a,b);
        else
            cout<<ask(a,b)<<'\n';
    }
    return 0;
}

终于补完了区间NAND的板子

要加油哦~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值