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;
}
很久以前的奇怪但现在依旧成立的签名
attack is our red sun $$\color{red}{\boxed{\color{red}{attack\ is\ our\ red\ sun}}}$$ ------------------------------------------------------------------------------------------------------------------------