【XSY3434】【UOJ431】time map(线段树维护位运算)

首先注意到一个性质:如果我们把权值相同且相邻的点归为一个连通块的话,那么一个叶子节点往上会经过至多 \(O(\log V)\) 个连通块(因为父亲节点一定是儿子节点的子集)。

又注意到属于同一个连通块内的点,走的方向都是一样的。

那么如果我们能动态维护点的权值,询问从某个点开始走时,假设它会往左走(那么在它所属的连通块内它就一直会往左走),那么我们可以二分出它往左走一直会走到哪(显然这段路上每个点的权值都是相同的,也就是说我们只需要算出这段路的长度即可知道这段路对答案的贡献),然后再跳到下一个连通块。这样做单次是 \(O(\log n\log V)\) 的(暂不考虑单点查询某个点权值的时间)。

那么现在问题变成了如何动态维护点的权值。

结果发现这棵线段树竟然是假的,我们完全可以把操作转化到普通线段树上。要维护的是:区间操作 or,and,xor 以及区间查询 and。

理论上是可以实现的:or0,or1,and0,and1,xor0,xor1 这六种操作实际上对应的是四种操作:不变、取反、置1、置0,而且这四种操作任意两种合并之后也还是这四种操作之一(意思是可以懒标记可以合并)。

而当一个区间内的数都进行这四种操作中的某一种时,我们需要动态维护这个区间内的数的 and。而由于有取反这一种操作,我们需要同时维护这个区间的 or 以更新 and。

如果一位一位地合并的话,单次合并和更新的时间复杂度为 \(O(\log V)\)

实际上我们可以用压位+位运算加速:用 00 表示不变,01 表示取反,10 表示置0,11 表示置1,我们用 \(+\) 表示合并,于是有:

\[\begin{aligned} ab+00&=ab\\ ab+01&=ab\oplus 01\\ ab+10&=10\\ ab+11&=11 \end{aligned} \]

于是可以得到一个用二进制加速合并的方法:\(ab+cd=(a|c,(b\&(\overline{c}))\oplus d)\),其中 \(\overline{c}\) 为取反操作。

而更新的时候,同样可以得到用二进制加速合并的方法:(设原来的 and,or 分别为 x,y,和操作 ab 合并):\((x\&\overline{a}\&\overline{b})\oplus(\overline{y}\&\overline{a}\&b)\oplus (a\&b)\)

按道理 00,01,10,11 四个二进制数和四种操作的对应是可以任意的,因为二进制加速实际上是通过二进制运算加速分类讨论的过程。

那么修改和查询在线段树上的复杂度都是 \(O(\log n)\) 的了。

实际上一开始说的二分可以在线段树上进行:我们实际上找的是最小的 \(p\) 满足 \(\operatorname{and}[l,p]=\operatorname{and}[l,r]\)(一直往左走)。

总时间复杂度 \(O(n+q_a\log n+q_b\log n\log V)\),其中 \(q_a\) 为修改次数,\(q_b\) 为询问次数。

#include<bits/stdc++.h>

#define N 1000010
#define fi first
#define se second
#define ll long long
#define lc(u) ch[u][0]
#define rc(u) ch[u][1]
#define INF 0x7fffffff
#define pii pair<int,int>
#define mk(a,b) make_pair(a,b)

using namespace std;

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

struct tag
{
    int a,b;
    tag(){};
    tag(int aa,int bb){a=aa,b=bb;}
};

tag operator + (tag a,tag b)
{
    return tag(a.a|b.a,((~b.a)&a.b)^b.b);
}

struct msg
{
    int x,y;
    msg(){};
    msg(int xx,int yy){x=xx,y=yy;}
};

msg operator + (msg a,tag b)
{
    return msg((a.x&(~b.a)&(~b.b))^((~a.y)&(~b.a)&b.b)^(b.a&b.b),(a.y&(~b.a)&(~b.b))^((~a.x)&(~b.a)&b.b)^(b.a&b.b));
}

msg operator + (msg a,msg b)
{
    return msg(a.x&b.x,a.y|b.y);
}

int n,q;
int rt,node,ch[N<<1][2],L[N<<1],R[N<<1],d[N<<1];
bool dtag[N<<2];
tag lazy[N<<2];
msg sum[N<<2];

vector<pii>vl[N],vr[N];

void dfs(int &u,int l,int r,int dep)
{
    u=++node;
    L[u]=l,R[u]=r;
    d[u]=dep;
    if(l==r)
    {
        vr[r].push_back(mk(l,u));
        vl[l].push_back(mk(r,u));
        return;
    }
    int mid=read();
    vr[r].push_back(mk(l,u));
    dfs(lc(u),l,mid,dep+1),dfs(rc(u),mid+1,r,dep+1);
    vl[l].push_back(mk(r,u));
}

void up(int k)
{
    sum[k]=sum[k<<1]+sum[k<<1|1];
}

void build(int k,int l,int r)
{
    if(l==r)
    {
        int val=read();
        sum[k]=msg(val,val);
        return;
    }
    int mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    up(k);
}

void downn(int k,tag now)
{
    sum[k]=sum[k]+now;
    if(!dtag[k]) lazy[k]=now,dtag[k]=1;
    else lazy[k]=lazy[k]+now;
}

void down(int k)
{
    if(dtag[k])
    {
        downn(k<<1,lazy[k]);
        downn(k<<1|1,lazy[k]);
        dtag[k]=0;
    }
}

void update(int k,int l,int r,int ql,int qr,tag now)
{
    if(ql<=l&&r<=qr)
    {
        downn(k,now);
        return;
    }
    down(k);
    int mid=(l+r)>>1;
    if(ql<=mid) update(k<<1,l,mid,ql,qr,now);
    if(qr>mid) update(k<<1|1,mid+1,r,ql,qr,now);
    up(k);
}

int queryand(int k,int l,int r,int ql,int qr)
{
    if(ql<=l&&r<=qr) return sum[k].x;
    down(k);
    int mid=(l+r)>>1;
    if(qr<=mid) return queryand(k<<1,l,mid,ql,qr);
    if(ql>mid) return queryand(k<<1|1,mid+1,r,ql,qr);
    return queryand(k<<1,l,mid,ql,qr)&queryand(k<<1|1,mid+1,r,ql,qr);
}

int queryl(int k,int l,int r,int ql,int qr,int &land,int cmp)
{
    if(ql<=l&&((land&sum[k].x)>cmp))
    {
        land&=sum[k].x;
        return -1;
    }
    if(l==r) return l;
    down(k);
    int mid=(l+r)>>1;
    if(ql<=mid)
    {
        int tmp=queryl(k<<1,l,mid,ql,qr,land,cmp);
        if(tmp!=-1) return tmp;
    }
    return queryl(k<<1|1,mid+1,r,ql,qr,land,cmp);
}

int queryr(int k,int l,int r,int ql,int qr,int &land,int cmp)
{
    if(r<=qr&&((land&sum[k].x)>cmp))
    {
        land&=sum[k].x;
        return -1;
    }
    if(l==r) return l;
    down(k);
    int mid=(l+r)>>1;
    if(qr>mid)
    {
        int tmp=queryr(k<<1|1,mid+1,r,ql,qr,land,cmp);
        if(tmp!=-1) return tmp;
    }
    return queryr(k<<1,l,mid,ql,qr,land,cmp);
}

int main()
{
    n=read(),q=read();
    build(1,1,n);
    dfs(rt,1,n,1);
    while(q--)
    {
        int opt=read(),l=read(),r=read();
        if(opt<=3)
        {
            int val=read();
            if(opt==1) update(1,1,n,l,r,tag(~val,0));
            if(opt==2) update(1,1,n,l,r,tag(val,val));
            if(opt==3) update(1,1,n,l,r,tag(0,val));
        }
        else
        {
            int Ll=l,Rr=r;
            int u=vl[l][lower_bound(vl[l].begin(),vl[l].end(),mk(r,0))-vl[l].begin()].se;
            assert(L[u]==Ll&&R[u]==Rr);
            ll ans=0;
            while(u)
            {
                l=L[u],r=R[u];
                int val=queryand(1,1,n,l,r);
                bool popc=__builtin_popcount(val)&1;
                if(popc)
                {
                    int fuck=INF;
                    int p=queryl(1,1,n,l,r,fuck,val);
                    int v=vl[l][lower_bound(vl[l].begin(),vl[l].end(),mk(p,0))-vl[l].begin()].se;
                    ans+=1ll*val*(d[v]-d[u]+1);
                    u=lc(v);
                }
                else
                {
                    int fuck=INF;
                    int p=queryr(1,1,n,l,r,fuck,val);
                    int v=vr[r][upper_bound(vr[r].begin(),vr[r].end(),mk(p,node+1))-vr[r].begin()-1].se;
                    ans+=1ll*val*(d[v]-d[u]+1);
                    u=rc(v);
                }
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}
posted @ 2022-10-30 12:19  ez_lcw  阅读(49)  评论(0编辑  收藏  举报