博客园 首页 私信博主 显示目录 隐藏目录 管理 动画

COGS. 2638. 数列操作(吉司机线段树)

题面链接
(COGS什么时候没的... 记得还能下数据挺良心的)


\(Description\)
给定长为\(n\)数列,需支持区间\(\text{or}\)\(\text{and}\)、求区间最大值。
\(n,m\leq10^5\)

\(Solution\)
考虑更简单的情况:所有操作区间为\([1,n]\)
如果\(\text{and},\text{or}\)的值随机,那么几次之后所有数就全相同了。
如果不随机,至少在\(\text{and}\)中为\(1\)\(\text{or}\)中为\(0\)的这些位都全相同了。
对于不同的二进制位直接对所有数求最大值。

(上面的讨论其实没什么用,不过jls这么讲的)
下面可能写错了 我有空再改改 直接看代码吧
若区间不为\([1,n]\),维护一个\(same\)表示某区间所有数都相同的那些位,以及区间最大值\(mx\)
若当前修改 \(\text{and}\)中为\(1\)的位 或 \(\text{or}\)中为\(0\)的位 是当前节点\(x\)\(same[x]\)的子集,就可以直接整体修改,而且就是对该区间所有\(mx\)加上 \(改后的值-mx[x]\)。直接打区间加减的\(tag\)修改\(mx\)即可。
所以,直接用吉司机线段树的写法,记\(\text{and}\)中为\(1\)的位 或 \(\text{or}\)中为\(0\)的位集为\(s\),若当前区间是修改区间的子区间 且 s&same[x]=s,则\(O(1)\)进行区间加/减;否则递归子区间。
查询时可以直接查区间\(mx\)

复杂度(🌿没好好听jls讲),应该也是\(O(n\log^2n)\)常表现为\(O(n\log n)\)的?

PS:关于暴力写法,涉及到两种\(tag\),需要先定义标记的优先级(先下放哪个)。优先级低的标记修改直接改,优先级高的标记修改时,需要用优先级高(先下放)的修改一下优先级低的(后下放)的。
如区间加、乘,若先下放乘再加,则加操作时直接加,乘操作时加标记需要乘一下。
(普及组知识让我再复习一下)


#include <bits/stdc++.h>
#define pc putchar
#define gc() getchar()
typedef long long LL;
const int N=2e5+6,INF=2147483647;

int read();
struct Segment_Tree
{
	#define S N<<2
	#define ls rt<<1
	#define rs rt<<1|1
	#define lson l,m,ls
	#define rson m+1,r,rs
	int same[S],mx[S],tag[S];
	#undef S

	#define Upd(rt,v) mx[rt]+=v, tag[rt]+=v
	#define Update(rt) mx[rt]=std::max(mx[ls],mx[rs]), same[rt]=same[ls]&same[rs]&(~(mx[ls]^mx[rs]))
	inline void PushDown(int rt)
	{
		if(tag[rt]) Upd(ls,tag[rt]), Upd(rs,tag[rt]), tag[rt]=0;
	}
	void Build(int l,int r,int rt)
	{
		if(l==r) {same[rt]=INF, mx[rt]=read(); return;}
		int m=l+r>>1;
		Build(lson), Build(rson), Update(rt);
	}
	void Modify_AND(int l,int r,int rt,int L,int R,int v)
	{
		if(l==r) {mx[rt]&=v; return;}
		if(L<=l && r<=R && (((~v)&INF)&same[rt])==((~v)&INF))
		{
			int t=(mx[rt]&v)-mx[rt];
			Upd(rt,t); return;
		}
		PushDown(rt);
		int m=l+r>>1;
		if(L<=m) Modify_AND(lson,L,R,v);
		if(m<R) Modify_AND(rson,L,R,v);
		Update(rt);
	}
	void Modify_OR(int l,int r,int rt,int L,int R,int v)
	{
		if(l==r) {mx[rt]|=v; return;}
		if(L<=l && r<=R && (v&same[rt])==v)
		{
			int t=(mx[rt]|v)-mx[rt];
			Upd(rt,t); return;
		}
		PushDown(rt);
		int m=l+r>>1;
		if(L<=m) Modify_OR(lson,L,R,v);
		if(m<R) Modify_OR(rson,L,R,v);
		Update(rt);
	}
	int Query(int l,int r,int rt,int L,int R)
	{
		if(L<=l && r<=R) return mx[rt];
		PushDown(rt);
		int m=l+r>>1;
		if(L<=m)
			if(m<R) return std::max(Query(lson,L,R),Query(rson,L,R));
			else return Query(lson,L,R);
		return Query(rson,L,R);
	}
}T;

inline int read()
{
	int now=0,f=1; char c=gc();
	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now*f;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("1.in","r",stdin);
	freopen("1.out","w",stdout);
#endif

	int n=read(),Q=read();
	#define S 1,n,1
	T.Build(S);
	for(int opt,L,R,x; Q--; )
	{
		opt=read(), L=read(), R=read();
		switch(opt)
		{
			case 1: x=read(), T.Modify_AND(S,L,R,x); break;
			case 2: x=read(), T.Modify_OR(S,L,R,x); break;
			case 3: printf("%d\n",T.Query(S,L,R)); break;
		}
	}

	return 0;
}
posted @ 2021-02-21 22:55  SovietPower  阅读(138)  评论(0编辑  收藏  举报