• 【UOJ671】【UNR #5】诡异操作(线段树+位运算)


    题目链接

    • 给定一个长度为 \(n\) 的序列 \(a_{1\sim n}\)
    • \(q\) 次操作,分为三种:区间除以 \(v\) 并向下取整;区间向 \(v\) 按位与;询问区间和。
    • \(1\le n\le3\times10^5\)\(1\le q\le2\times10^5\)\(0\le a_i,v < 2^{128}\)

    常规思路

    首先容易发现两种操作都只会使 \(a_i\) 越来越小,所以对于任意一个 \(a_i\),忽略掉 \(v=1\) 的操作后,在 \(O(\log a_i)\) 次除法操作后便会变成 \(0\)。所以,对于区间除法,可以直接暴力修改。

    而对于区间按位与操作,只要分别维护每一位,则 \(v\)\(1\) 的位不变,\(v\)\(0\) 的位清零。

    这样的复杂度是 \(O((n\log V+q\log n)\log V)\),但由于 \(\log V\) 非常大,会 TLE。

    奇怪的优化操作

    好像之前也写过区间按位与操作的题目,然而并不知道这个优化。

    常规思路是对于每一位维护数的个数,显然个数不可能超过 \(n\)。因此,把个数看作二进制数,则相当于维护了一个 \(\log V\times \log n\) 的表格。

    考虑将表格翻转,变成一个 \(\log n\times\log V\) 的表格。也就是用 \(\log n\) 个变量,第 \(i\) 个变量压位维护每一位答案在二进制下第 \(i\) 位的值。

    发现这样一来就有很多好处:首先,对于单独一个数需要维护的就是它自身,不需要再花 \(O(\log V)\) 的时间做二进制拆解;其次,现在只需维护 \(\log n\) 个变量,而不是 \(\log V\) 个变量,维护的时间复杂度也就变成了 \(\log n\),询问答案的时候也只要扫一遍求出 \(V_i\times2^i\) 之和即可。

    所以现在的时间复杂度就被优化成了 \(O(n\log V+q\log^2n)\)

    余下的一个问题是如何上传信息,也就是实现 \(\log V\) 个压在一起的二进制数的加法。

    判断进位只要判断两个相加的数以及上次进位的数是否有至少两个 \(1\)(两两按位与再按位或),而这位剩余值可以直接用不进位加法(异或)计算。

    写的时候注意常数。像我一开始代码写得太丑,结果卡了一个上午才卡过去。

    代码:\(O(n\log V+q\log^2n)\)

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 300000
    #define SZ 1500000
    #define INT __uint128_t
    using namespace std;
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	I void readINT(INT& x) {x=0;W(isspace(oc=tc()));W((x<<=4)|=isdigit(oc)?(oc&15):oc-87,!isspace(oc=tc()));}
    	I void writeINT(INT x) {W(OS[++OT]=(x&15)<=9?48|(x&15):87+(x&15),x>>=4);W(OT) pc(OS[OT--]);pc('\n');}
    }using namespace FastIO;
    int n;INT a[N+5];
    class SegmentTree
    {
    	private:
    		#define PT CI l=1,CI r=n,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		#define PD(rt) (~F[rt]&&(T(rt<<1,F[rt]),T(rt<<1|1,F[rt]),F[rt]=-1))
    		int G[N<<2];INT pV[SZ],*nw,*V[N<<2],F[N<<2],E[N<<2];
    		I void PU(CI x)//上传信息
    		{
    			E[x]=E[x<<1]|E[x<<1|1],G[x]=max(G[x<<1],G[x<<1|1]);INT *A=V[x<<1],*B=V[x<<1|1],C=0;RI i;
    			for(i=0;i<=min(G[x<<1],G[x<<1|1]);++i,++A,++B) V[x][i]=*A^*B^C,C=(*A&*B)|((*A|*B)&C);//异或计算剩余值,按位与计算进位值
    			for(;i<=G[x<<1];++i,++A) V[x][i]=*A^C,C&=*A;for(;i<=G[x<<1|1];++i,++B) V[x][i]=*B^C,C&=*B;
    			C&&(V[x][++G[x]]=C);//如果最后还有进位,将位数加1
    		}
    		I void T(CI x,INT v) {if((E[x]&v)==E[x]) return;E[x]&=v,F[x]&=v;for(RI i=0;i<=G[x];++i) V[x][i]&=v;W(~G[x]&&!V[x][G[x]]) --G[x];}//将x子树内按位与v
    		I INT Calc(CI x) {INT t=0;for(RI i=G[x];~i;--i) (t<<=1)+=V[x][i];return t;}//计算值
    	public:
    		I SegmentTree() {nw=pV;}
    		I int Bd(PT)//建树,同时分配内存
    		{
    			if(F[rt]=-1,l==r) return V[rt]=nw,++nw,G[rt]=(E[rt]=V[rt][0]=a[l])?0:-1,0;
    			RI mid=l+r>>1,d=max(Bd(LT),Bd(RT))+1;return V[rt]=nw,nw+=d+1,PU(rt),d;
    		}
    		I void Div(CI L,CI R,INT v,PT)//区间除法
    		{
    			if(!~G[rt]) return;if(l==r) return (void)(G[rt]=(E[rt]=(V[rt][0]/=v))?0:-1);
    			RI mid=l+r>>1;PD(rt),L<=mid&&(Div(L,R,v,LT),0),R>mid&&(Div(L,R,v,RT),0),PU(rt);
    		}
    		I void And(CI L,CI R,INT v,PT)//区间按位与
    		{
    			if(!~G[rt]||(E[rt]&v)==E[rt]) return;if(L<=l&&r<=R) return T(rt,v);RI mid=l+r>>1;PD(rt),L<=mid&&(And(L,R,v,LT),0),R>mid&&(And(L,R,v,RT),0),PU(rt);
    		}
    		I INT Q(CI L,CI R,PT)//区间求和
    		{
    			if(!~G[rt]) return 0;if(L<=l&&r<=R) return Calc(rt);RI mid=l+r>>1;PD(rt);return (L<=mid?Q(L,R,LT):0)+(R>mid?Q(L,R,RT):0);
    		}
    }S;
    int main()
    {
    	RI Qt,i,op,x,y;INT v;for(read(n,Qt),i=1;i<=n;++i) readINT(a[i]);S.Bd();
    	W(Qt--) read(op,x,y),op^3?(readINT(v),op==1?(void)(v^1&&(S.Div(x,y,v),0)):S.And(x,y,v)):writeINT(S.Q(x,y));return clear(),0;
    }
    
  • 相关阅读:
    CSS中的小知识
    网络基础 中的osi七层 协议
    pickle的使用
    max()的key的运用
    read,readline,readlines的区别
    print()控制台输出带颜色的方法
    写项目时bin目录下的start中的细节(路径问题的解决)
    使用hashlib密文存储实例
    固态硬盘使用f2fs作为根分区安装linux
    工厂方法(Factory Method)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/UOJ671.html
Copyright © 2020-2023  润新知