• CF1004F Sonya and Bitwise OR(线段树平衡复杂度+or 前缀性质)


    CF1004F Sonya and Bitwise OR

    有一个长度为 \(n\) 的数组 \(\{a\}\),有 \(m\) 次操作,又给定一个数 \(x\),有两类操作:

    • 1 i y\(a_i\) 改为 \(y\)
    • 2 l r 查询有多少个区间 \([L,R]\) 满足 \(a_{L\dots R}\) 的按位或 \(\tt{or}\) 大于等于 \(x\)

    \(1\le n,m\le 10^5;0\le a_i,x,y\le 2^{20}\)

    先考虑如果只有询问操作怎么做。

    那可以算出这段区间的中点开始,算出 \([l,m]\) 的后缀 \(\tt{or}\)\([m+1,r]\) 的前缀 \(\tt{or}\)

    那么可以通过双指针来求出每一个符合条件的区间,如果改为多次询问就用树状数组记下来,一次 \(\log n\) 查询,总复杂度 \(n\log n\)

    \(\bigstar\texttt{Hint}\):到这一步之后就一直在想用 CDQ 分治来完成对询问的处理,但其实因为是单点修改,多次询问,可以思考用线段树维护这一过程。而且线段树自带分治功能。

    \(\color{yellow}{\bigstar\texttt{Trick}}\):一个非常重要的性质,在一段数的 \(\tt{or}\) 前缀中,只可能出现 \(\log_2 a\) 个可能的值。每次最多让一个 \(0\) 位变成 \(1\),因此最多只会有 \(\log_2a\) 段。

    那么可以在线段树中维护区间内的前后缀 \(\tt{or}\) 段,每个区间都不会超过 \(20\) 个,同时记录这个区间内的合法子区间数量,像线段树一样修改询问即可。

    总结:

    最开始的 CDQ 想法大体方向是正确的,和正解都利用了分治。但是存在修改操作后,对区间分治就不能够在 CDQ 上进行,必须另寻他路。

    那么接下来就应当考虑在线解决这个问题,这道题的区间是可以合并的,而平衡复杂度的思想就让我们想到了线段树

    最后这个经典的 \(\tt{Trick}\) 我不能够熟练使用,下次就知道了。

    #define Maxn 100005
    int n,m,X;
    int a[Maxn];
    struct SUB
    {
    	SUB(int Val=0,int Pl=0,int Pr=0):val(Val),pl(Pl),pr(Pr){}
    	int val,pl,pr;
    };
    SUB tpre[100],tsuf[100];
    struct TREE
    {
    	ll sum;
    	vector<SUB> pre,suf;
    	TREE(){ sum=0,pre.clear(),suf.clear(); }
    	inline void push(int x,int Nu)
    	{
    		pre.clear(),suf.clear(),sum=(x>=X),
    		pre.pb(SUB(x,Nu,Nu)),suf.pb(SUB(x,Nu,Nu));
    	}
    }tree[Maxn<<2];
    inline TREE merge(TREE L,TREE R)
    {
    	int ppre=0,psuf=0,tl=L.pre.back().val,tr=R.suf.front().val;
    	for(SUB v:L.pre) tpre[++ppre]=v;
    	for(SUB v:R.pre) tpre[++ppre]=SUB(v.val|tl,v.pl,v.pr);
    	for(SUB v:L.suf) tsuf[++psuf]=SUB(v.val|tr,v.pl,v.pr);
    	for(SUB v:R.suf) tsuf[++psuf]=v;
    	tpre[ppre+1]=SUB(-1,0,0),tsuf[psuf+1]=SUB(-1,0,0);
    	TREE ret; ret.sum=L.sum+R.sum;
    	for(int nl=0,nr=0,sl=L.suf.size()-1,sr=R.pre.size()-1;nl<=sl;nl++)
    	{
    		while(nr<=sr && (L.suf[nl].val|R.pre[nr].val)<X) nr++;
    		if(nr>sr) break;
    		ret.sum+=1ll*(R.pre[sr].pr-R.pre[nr].pl+1)*
    			(L.suf[nl].pr-L.suf[nl].pl+1);
    	}
    	for(int i=2,Last=0;i<=ppre+1;i++) if(tpre[i].val!=tpre[i-1].val)
    		assert(tpre[Last+1].val==tpre[i-1].val),
    		ret.pre.pb(SUB(tpre[i-1].val,tpre[Last+1].pl,tpre[i-1].pr)),Last=i-1;
    	for(int i=2,Last=0;i<=psuf+1;i++) if(tsuf[i].val!=tsuf[i-1].val)
    		assert(tsuf[Last+1].val==tsuf[i-1].val),
    		ret.suf.pb(SUB(tsuf[i-1].val,tsuf[Last+1].pl,tsuf[i-1].pr)),Last=i-1;
    	assert(ret.pre.size()<=30 && ret.suf.size()<=30);
    	return ret;
    }
    void build(int p,int nl,int nr)
    {
    	if(nl==nr) { tree[p].push(a[nl],nl); return; }
    	int mid=(nl+nr)>>1;
    	build(p<<1,nl,mid),build(p<<1|1,mid+1,nr);
    	tree[p]=merge(tree[p<<1],tree[p<<1|1]);
    }
    void change(int p,int nl,int nr,int x,int k)
    {
    	if(nl==nr) { tree[p].push(k,x); return; }
    	int mid=(nl+nr)>>1;
    	if(mid>=x) change(p<<1,nl,mid,x,k);
    	else change(p<<1|1,mid+1,nr,x,k);
    	tree[p]=merge(tree[p<<1],tree[p<<1|1]);
    }
    TREE query(int p,int nl,int nr,int l,int r)
    {
    	if(nl>=l && nr<=r) return tree[p];
    	int mid=(nl+nr)>>1;
    	if(mid>=l && mid<r)
    		return merge(query(p<<1,nl,mid,l,r),query(p<<1|1,mid+1,nr,l,r));
    	else if(mid>=l) return query(p<<1,nl,mid,l,r);
    	else return query(p<<1|1,mid+1,nr,l,r);
    }
    int main()
    {
    	n=rd(),m=rd(),X=rd();
    	for(int i=1;i<=n;i++) a[i]=rd();
    	build(1,1,n);
    	for(int i=1,opt,x,y;i<=m;i++)
    	{
    		opt=rd(),x=rd(),y=rd();
    		if(opt==1) change(1,1,n,x,y);
    		else printf("%lld\n",query(1,1,n,x,y).sum);
    	}
    	return 0;
    }
    
  • 相关阅读:
    UOJ168. 【UR #11】元旦老人与丛林
    luogu3308,LOJ 2196 [SDOI2014]LIS
    CF1349F2. Slime and Sequences (Hard Version)
    6210. wsm
    欧拉数学习小记
    CF1508F. Optimal Encoding
    CF1508C. Complete the MST
    联合省选2021 游记
    一. Docker介绍
    Elasticsearch
  • 原文地址:https://www.cnblogs.com/EricQian/p/16697902.html
Copyright © 2020-2023  润新知