首页 > 其他分享 >【XSY3434】【UOJ431】time map(线段树维护位运算)

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

时间:2022-10-30 12:35:03浏览次数:45  
标签:UOJ431 map ab return log int ch XSY3434 define

首先注意到一个性质:如果我们把权值相同且相邻的点归为一个连通块的话,那么一个叶子节点往上会经过至多 \(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;
}

标签:UOJ431,map,ab,return,log,int,ch,XSY3434,define
From: https://www.cnblogs.com/ez-lcw/p/16840979.html

相关文章

  • 0082-Go-关联类型 map
    环境Time2022-08-23Go1.19前言说明参考:https://gobyexample.com/maps目标使用Go语言的关联类型map。新建mappackagemainimport"fmt"funcmain(){......
  • 60-ES10-数组方法扩展-flat与flatMap
     ......
  • [Typescript] 79. Medium - MapTypes
    Implement MapTypes<T,R> whichwilltransformtypesinobjectTtodifferenttypesdefinedbytypeRwhichhasthefollowingstructuretypeStringToNumber=......
  • .net Core MVC 2.0项目中如果引入AutoMapper
    第一步骤:Nuget中引入AutoMapper依赖注入包 第二步:创建一个类并继承Profile基类,并创建映射,如果需要互相映射需要调用ReverseMap()方法,如果需求忽略某些字段不进行映射,......
  • 网络安全(一):信息收集之玩转nmap(理论篇)
    更新时间2022年09月06日16:20:10完成nmap介绍,目标选择,主机发现部分2022年10月28日21:19:20完成最基本的内容,端口扫描,版本和系统探测,安全其他等打算的更新计划:更多......
  • Map之“获取map中的key流转为List”
    一.获取map中的key转为List注意这里可以获取map中所有的key来转换为List,这样后很多方案就不需要另外查询出来处理了代码@Testpublicvoidtest(){......
  • 【THM】Nmap Advanced Port Scans(Nmap高级端口扫描)-学习
    本文相关的TryHackMe实验房间链接:https://tryhackme.com/room/nmap03介绍在Nmap基本端口扫描中,我们介绍了TCP标志以及TCP3次握手的过程。要启动TCP连接,首先要给第......
  • TreeMap
    (1)TreeMap跟TreeSet底层原理一样,都是红黑树结构的。(2)由键决定特性:不重复、无索引、可排序。(3)可排序:对键进行排序。(4)注意:默认按照键的从小到大进行排序,也可以自己规定键的......
  • JDK8新特性、Lambda表达式、StreamApi
     packagecom.cn86trading.trading;importorg.junit.jupiter.api.Test;/***CreatedwithIntelliJIDEA.**@author:小黑*@version:1.0*@Project......
  • HashMap
    (1)特点1.HashMap是Map里面的一个实现类;2.没有额外需要学习的特有方法,直接使用Map里面的方法就可以了;3.特点都是由键决定的:无序、不重复、无索引;4.HashMap跟HashSet底层......