线段树学习笔记
普通线段树
CF242E XOR on Segment
题意:区间异或上一个数&区间求和
可以考虑拆位,然后每一个位都写一个线段树。线段树维护区间内部有多少个1,tag维护下面这个区间是否异或1(即01反转)。
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;
inline int read() {
register int x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
int n,a[N],m;
bool bit(int s,int i) {return s&(1ll<<i);}
struct segment_tree {
struct node {int cnt,tag;} t[N<<2];
void build(int p,int l,int r,int k) {
if(l==r) {t[p].cnt=bit(a[l],k); return;}
build(p*2,l,(l+r)/2,k), build(p*2+1,(l+r)/2+1,r,k);
t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
}
void pushdown(int p,int l,int r) {
if(t[p].tag) {
t[p*2].tag^=1, t[p*2+1].tag^=1;
t[p*2].cnt=(l+r)/2-l+1-t[p*2].cnt;
t[p*2+1].cnt=r-(l+r)/2-t[p*2+1].cnt;
t[p].tag=0;
}
}
void modify(int p,int x,int y,int l,int r) {
int mid=(l+r)/2;
if(x==l&&y==r) {t[p].cnt=r-l+1-t[p].cnt, t[p].tag^=1; return;}
pushdown(p,l,r);
if(y<=mid) modify(p*2,x,y,l,mid);
else if(x>mid) modify(p*2+1,x,y,mid+1,r);
else modify(p*2,x,mid,l,mid), modify(p*2+1,mid+1,y,mid+1,r);
t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
}
int query(int p,int x,int y,int l,int r) {
int mid=(l+r)/2;
if(x==l&&y==r) return t[p].cnt;
pushdown(p,l,r);
if(y<=mid) return query(p*2,x,y,l,mid);
else if(x>mid) return query(p*2+1,x,y,mid+1,r);
else return query(p*2,x,mid,l,mid)+query(p*2+1,mid+1,y,mid+1,r);
}
}st[21];
signed main() {
n=read();
rep(i,1,n) a[i]=read();
rep(h,0,19) st[h].build(1,1,n,h);
m=read();
rep(i,1,m) {
int opt=read(), l=read(), r=read();
if(opt==1) {
int ans=0;
rep(h,0,19) ans+=st[h].query(1,l,r,1,n)*(1ll<<h);
printf("%lld\n",ans);
} else {
int x=read();
rep(h,0,19) if(bit(x,h)) st[h].modify(1,l,r,1,n);
}
}
return 0;
}
CF620E New Year Tree
维护每个数有没有在区间出现过。由于数<=60,可以考虑直接状压。合并区间就直接or一下两个子区间即可。
#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
typedef long long ll;
const int N=4e5+9;
inline int read() {
register int x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
struct edge {int to,nxt;} e[N*2]; int hd[N],tot;
void add(int u,int v) {e[++tot]=(edge){v,hd[u]};hd[u]=tot;}
int n,a[N],m,dt[N],st[N],ed[N],tick;
void dfs(int u,int fa) {
dt[st[u]=++tick]=u;
for(int i=hd[u],v;i;i=e[i].nxt) {
if((v=e[i].to)==fa) continue;
dfs(v,u);
}
ed[u]=tick;
}
struct node {int l,r,tag; ll s;} t[N<<2];
void build(int p,int l,int r) {
t[p].l=l, t[p].r=r;
if(l==r) {t[p].s=(1ll<<a[dt[l]]); return;}
build(p*2,l,(l+r)/2), build(p*2+1,(l+r)/2+1,r);
t[p].s=t[p*2].s|t[p*2+1].s;
}
void pushdown(int p) {
t[p*2].tag=t[p*2+1].tag=t[p].tag;
t[p*2].s=t[p*2+1].s=(1ll<<t[p].tag);
t[p].tag=0;
}
void modify(int p,int x,int y,int c) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(l==x&&r==y) {t[p].s=(1ll<<c),t[p].tag=c; return;}
if(t[p].tag) pushdown(p);
if(y<=mid) modify(p*2,x,y,c);
else if(x>mid) modify(p*2+1,x,y,c);
else modify(p*2,x,mid,c), modify(p*2+1,mid+1,y,c);
t[p].s=t[p*2].s|t[p*2+1].s;
}
ll query(int p,int x,int y) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(l==x&&y==r) return t[p].s;
if(t[p].tag) pushdown(p);
if(y<=mid) return query(p*2,x,y);
else if(x>mid) return query(p*2+1,x,y);
else return query(p*2,x,mid)|query(p*2+1,mid+1,y);
}
int main() {
n=read(), m=read();
rep(i,1,n) a[i]=read();
rep(i,1,n-1) {
int u=read(), v=read();
add(u,v), add(v,u);
}
dfs(1,0);
build(1,1,n);
rep(i,1,m) {
int opt=read(), u=read();
if(opt==1) modify(1,st[u],ed[u],read());
else {
ll s=query(1,st[u],ed[u]); int ans=0;
rep(h,0,60) ans+=(bool)(s&(1ll<<h));
printf("%d\n",ans);
}
}
return 0;
}
CF438D The Child and Sequence
取模很快就会膜小,所以和花神一题相似,我们暴力修改,并判断最大值是否<x即可。
CF431E Chemistry Experiment
可以二分+线段树 \(O(n\log^2n)\)。但由于线段树本来就是个二叉结构,所以考虑直接线段树上二分。首先我们要把所有准备加水的试管的液体量变一致。所以我们可以判断(已有的sz+左子树的sz)乘上mid再减去(已有的sum+左子树的sum)是否不小于x,如果是那么就走左边。否则走右边。
要动态开点。
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;
inline int read() {
register int x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
int n,m,h[N],mx=1000000000;
int tot=1;
struct node {int ls,rs,sz,sum;} t[N*32];
void ins(int p,int l,int r,int x) {
int mid=(l+r)/2;
if(l==r) {t[p].sz++,t[p].sum+=x;return;}
if(x<=mid) ins(t[p].ls?t[p].ls:t[p].ls=++tot,l,mid,x);
else ins(t[p].rs?t[p].rs:t[p].rs=++tot,mid+1,r,x);
t[p].sz=t[t[p].ls].sz+t[t[p].rs].sz,
t[p].sum=t[t[p].ls].sum+t[t[p].rs].sum;
}
void del(int p,int l,int r,int x) {
int mid=(l+r)/2;
if(l==r) {t[p].sz--,t[p].sum-=x;return;}
if(x<=mid) del(t[p].ls,l,mid,x);
else del(t[p].rs,mid+1,r,x);
t[p].sz=t[t[p].ls].sz+t[t[p].rs].sz,
t[p].sum=t[t[p].ls].sum+t[t[p].rs].sum;
}
double query(int x) {
int l=0,r=mx,p=1,ssz=0,ssum=0;
while(l!=r) {
double mid=(l+r)/2;
if(t[p].ls&&(ssz+t[t[p].ls].sz)*(mid+0.9999)-ssum-t[t[p].ls].sum>=x) p=t[p].ls,r=(l+r)/2;
else {
if(t[p].ls) ssz+=t[t[p].ls].sz, ssum+=t[t[p].ls].sum;
p=t[p].rs, l=(l+r)/2+1;
}
}
return min(1.*(ssum+t[p].sum+x)/(ssz+t[p].sz),1.*(ssum+x)/ssz);
}
signed main() {
//freopen("CF431E.in","r",stdin);
//freopen("CF431E.out","w",stdout);
n=read(), m=read();
rep(i,1,n) ins(1,0,mx,h[i]=read());
rep(i,1,m) {
int opt=read(),p=read();
if(opt==1) del(1,0,mx,h[p]), ins(1,0,mx,h[p]=read());
else printf("%.5lf\n",query(p));
}
return 0;
}
CF240F TorCoder
首先重排后能成为回文串等价于出现奇数次数的字母不多于1个。对于区间 l,r,由于要最小字典序,所以我们把每个字母按顺序都在两边分别塞 \(cnt/2\) 个。对于那个奇数的,我们把它剩余的1个放在中间即可。对于每个字母,我们现在要求支持:区间查询个数和区间覆盖。可以给每个字母开个线段树解决。
#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;
inline int read() {
register int x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
int n,m,cnt[29];
char s[N];
struct segtree {
struct node {int l,r,cnt,tag;} t[N*4];
void build(int p,int l,int r,char c) {
int mid=((t[p].l=l)+(t[p].r=r))/2;
if(l==r) {t[p].cnt=(s[l]==c); return;}
build(p*2,l,mid,c), build(p*2+1,mid+1,r,c);
t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
}
void pushdown(int p) {
t[p*2].tag=t[p*2+1].tag=t[p].tag;
if(t[p].tag==-1) t[p*2].cnt=t[p*2+1].cnt=0;
else {
t[p*2].cnt=t[p*2].r-t[p*2].l+1;
t[p*2+1].cnt=t[p*2+1].r-t[p*2+1].l+1;
}
t[p].tag=0;
}
void modify(int p,int x,int y,int k) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(l==x&&r==y) {t[p].cnt=(k>0?r-l+1:0),t[p].tag=k; return;}
if(t[p].tag) pushdown(p);
if(y<=mid) modify(p*2,x,y,k);
else if(x>mid) modify(p*2+1,x,y,k);
else modify(p*2,x,mid,k), modify(p*2+1,mid+1,y,k);
t[p].cnt=t[p*2].cnt+t[p*2+1].cnt;
}
int query(int p,int x,int y) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(l==x&&r==y) return t[p].cnt;
if(t[p].tag) pushdown(p);
if(y<=mid) return query(p*2,x,y);
else if(x>mid) return query(p*2+1,x,y);
else return query(p*2,x,mid)+query(p*2+1,mid+1,y);
}
}st[29];
void print(int l=1,int r=n) {
rep(i,l,r)
rep(j,0,25)
if(st[j].query(1,i,i)) {putchar(j+'a');break;}
puts("");
}
int main() {
freopen("input.txt","r",stdin);
freopen("output.txt","w",stdout);
scanf("%d%d%s",&n,&m,s+1);
rep(i,0,25) st[i].build(1,1,n,i+'a');
rep(i,1,m) {
int l=read(), r=read(), odd=0;
rep(i,0,25) cnt[i]=st[i].query(1,l,r), odd+=cnt[i]%2;
if(odd>1) continue;
rep(i,0,25) st[i].modify(1,l,r,-1);
int pos=0;
rep(i,0,25) if(cnt[i]>1) {
int ycnt=cnt[i];
cnt[i]/=2;
st[i].modify(1,l+pos,l+pos+cnt[i]-1,1);
st[i].modify(1,r-pos-cnt[i]+1,r-pos,1);
pos+=cnt[i];
cnt[i]=ycnt;
}
if(odd) {
rep(i,0,25) if(cnt[i]%2==1) {
st[i].modify(1,l+pos,l+pos,1);
}
}
}
print();
return 0;
}
CF992E Nastya and King-Shamans
- 单点修改
- 询问是否有 \(i=\sum_{j=1}^{i-1}a_j\)
一般这种题都要对询问进行化简。我们发现,满足要求的 \(i\) 最多只有 \(\log\) 种可能,因为每发现一个可能,下一个就要是上一个的两倍及以上。那考虑如何遍历所有可能。从 \(k=1\) 开始,每次找到满足 \(s_x\ge 2s_k\) 的最小的 \(x\) 即可。这个可以线段树上二分实现。
#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=2e5+9;
inline int read() {
register int x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
int n,q,a[N];
long long s[N];
struct node {int l,r;long long x,tag;} t[N*4];
void build(int p,int l,int r) {
int mid=((t[p].l=l)+(t[p].r=r))/2;
if(l==r) {t[p].x=s[l]; return;}
build(p*2,l,mid), build(p*2+1,mid+1,r);
t[p].x=max(t[p*2].x,t[p*2+1].x);
}
void pushdown(int p) {
t[p*2].tag+=t[p].tag, t[p*2+1].tag+=t[p].tag;
t[p*2].x+=t[p].tag, t[p*2+1].x+=t[p].tag;
t[p].tag=0;
}
void add(int p,int x,int y,int k) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(l==x&&r==y) {t[p].x+=k,t[p].tag+=k; return;}
pushdown(p);
if(y<=mid) add(p*2,x,y,k);
else if(x>mid) add(p*2+1,x,y,k);
else add(p*2,x,mid,k), add(p*2+1,mid+1,y,k);
t[p].x=max(t[p*2].x,t[p*2+1].x);
}
int query(int p,long long k) {
if(t[p].l==t[p].r) return t[p].x>=k?p:-1;
pushdown(p);
if(t[p*2].x>=k) return query(p*2,k);
else if(t[p*2+1].x>=k) return query(p*2+1,k);
else return -1;
}
int main() {
n=read(), q=read();
rep(i,1,n) a[i]=read(), s[i]=s[i-1]+a[i];
build(1,1,n);
rep(i,1,q) {
int pos=read(), val=read();
add(1,pos,n,val-a[pos]);
a[pos]=val;
int k=1;long long sk=a[1];
while(k) {
int p=query(1,2*sk);
if(p==-1) {puts("-1");break;}
sk=t[p].x, k=t[p].l;
if(2*a[k]==sk) {printf("%d\n",k);break;}
}
}
return 0;
}
P2572 [SCOI2010]序列操作
一年多前看神仙题,一年多后看傻逼题。
维护区间0/1的个数,最长连续0/1的个数,左起最长连续0/1的个数,右起最长连续0/1的个数,然后tag打是覆盖0/1/不覆盖,以及区间是否要翻转。注意两个tag的问题。显然处理覆盖更加方便,所以我们另其tag的优先顺序为先赋值然后取反。操作比较复杂,所以会共同用的函数分开写成单独的函数调用方便。
#include<bits/stdc++.h>
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9;
inline int read() {
register int x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
int n,m,a[N];
struct node {
int l,r,len,c[2],ls[2],rs[2],s[2],tg1,tg2;
void cov(int x) {
c[x]=ls[x]=rs[x]=s[x]=len;
c[x^1]=ls[x^1]=rs[x^1]=s[x^1]=0;
tg1=x, tg2=0;
}
void flip() {
swap(c[0],c[1]), swap(s[0],s[1]),
swap(ls[0],ls[1]), swap(rs[0],rs[1]);
tg2^=1;
}
} t[N*4];
node pushup(node x,node y,node r) {
r.len=x.len+y.len;
rep(i,0,1) {
r.c[i]=x.c[i]+y.c[i];
r.ls[i]=(x.c[i^1]?x.ls[i]:x.len+y.ls[i]);
r.rs[i]=(y.c[i^1]?y.rs[i]:y.len+x.rs[i]);
r.s[i]=max(max(x.s[i],y.s[i]),x.rs[i]+y.ls[i]);
}
return r;
}
void pushdown(int p) {
if(t[p].tg1!=-1) t[p*2].cov(t[p].tg1),t[p*2+1].cov(t[p].tg1);
if(t[p].tg2) t[p*2].flip(), t[p*2+1].flip();
t[p].tg1=-1, t[p].tg2=0;
}
void build(int p,int l,int r) {
t[p].l=l, t[p].r=r, t[p].tg1=-1, t[p].tg2=0, t[p].len=t[p].r-t[p].l+1;
if(l==r) {t[p].cov(a[l]); return;}
build(p*2,l,(l+r)/2), build(p*2+1,(l+r)/2+1,r);
t[p]=pushup(t[p*2],t[p*2+1],t[p]);
}
void cov(int p,int x,int y,int k) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(x==l&&y==r) {t[p].cov(k); return;}
pushdown(p);
if(y<=mid) cov(p*2,x,y,k);
else if(x>mid) cov(p*2+1,x,y,k);
else cov(p*2,x,mid,k), cov(p*2+1,mid+1,y,k);
t[p]=pushup(t[p*2],t[p*2+1],t[p]);
}
void flip(int p,int x,int y) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(x==l&&y==r) {t[p].flip(); return;}
pushdown(p);
if(y<=mid) flip(p*2,x,y);
else if(x>mid) flip(p*2+1,x,y);
else flip(p*2,x,mid), flip(p*2+1,mid+1,y);
t[p]=pushup(t[p*2],t[p*2+1],t[p]);
}
node query(int p,int x,int y) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(x==l&&y==r) return t[p];
pushdown(p);
node tmp;
if(y<=mid) return query(p*2,x,y);
else if(x>mid) return query(p*2+1,x,y);
else return pushup(query(p*2,x,mid),query(p*2+1,mid+1,y),tmp);
}
int main() {
n=read(), m=read();
rep(i,1,n) a[i]=read();
build(1,1,n);
rep(i,1,m) {
int opt=read(), l=read()+1, r=read()+1;
if(opt==0) cov(1,l,r,0);
else if(opt==1) cov(1,l,r,1);
else if(opt==2) flip(1,l,r);
else if(opt==3) printf("%d\n",query(1,l,r).c[1]);
else printf("%d\n",query(1,l,r).s[1]);
}
return 0;
}
后记:tmd一堆细节错误调死人,再也不叫他傻逼题了
CF1114F Please, another Queries on Array?
需要发现一个重要的东西:质数个数其实很少(62)个,所以直接状压就行了。
知道区间质数有哪些就可以轻而易举地算出答案了
复杂度 \(O(n\log^2 n+62n)\)。
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=4e5+9,mod=1e9+7;
typedef pair<int,int> pii;
inline int read() {
register int x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
int n,q,a[N],pr[N],inv[N],cnt;
bool vst[N];
int ksm(int a,int b) {
if(b==0) return 1;
else return ksm(a*a%mod,b/2)*(b%2==1?a:1)%mod;
}
void sieve() {
rep(i,2,300) {
if(vst[i]) continue;
pr[++cnt]=i, inv[cnt]=ksm(i,mod-2);
for(int j=2;i*j<=300;j++) vst[i*j]=1;
}
}
struct node {int l,r,s,tg1,tg2,prod;} t[N*4];
void build(int p,int l,int r) {
int mid=((t[p].l=l)+(t[p].r=r))/2;
if(l==r) {
rep(i,1,cnt) if(a[l]%pr[i]==0) t[p].s+=(1ll<<i);
t[p].prod=a[l];
t[p].tg1=0, t[p].tg2=1;
return;
}
build(p*2,l,mid), build(p*2+1,mid+1,r);
t[p].s=t[p*2].s|t[p*2+1].s, t[p].prod=t[p*2].prod*t[p*2+1].prod%mod;
t[p].tg1=0, t[p].tg2=1;
}
void pushdown(int p) {
t[p*2].s|=t[p].tg1, t[p*2+1].s|=t[p].tg1;
t[p*2].tg1|=t[p].tg1, t[p*2+1].tg1|=t[p].tg1;
int len=t[p*2].r-t[p*2].l+1;
t[p*2].prod=(t[p*2].prod*ksm(t[p].tg2,len))%mod;
t[p*2].tg2=(t[p*2].tg2*t[p].tg2)%mod;
len=t[p*2+1].r-t[p*2+1].l+1;
t[p*2+1].prod=(t[p*2+1].prod*ksm(t[p].tg2,len))%mod;
t[p*2+1].tg2=(t[p*2+1].tg2*t[p].tg2)%mod;
t[p].tg1=0, t[p].tg2=1;
}
void modify(int p,int x,int y,int g,int k) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(l==x&&r==y) {
t[p].s|=k, t[p].tg1|=k;
t[p].prod=t[p].prod*ksm(g,r-l+1)%mod,
t[p].tg2=t[p].tg2*g%mod;
return;
}
pushdown(p);
if(y<=mid) modify(p*2,x,y,g,k);
else if(x>mid) modify(p*2+1,x,y,g,k);
else modify(p*2,x,mid,g,k), modify(p*2+1,mid+1,y,g,k);
t[p].s=t[p*2].s|t[p*2+1].s, t[p].prod=t[p*2].prod*t[p*2+1].prod%mod;
}
pii query(int p,int x,int y) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(l==x&&r==y) return make_pair(t[p].s,t[p].prod);
pushdown(p);
if(y<=mid) return query(p*2,x,y);
else if(x>mid) return query(p*2+1,x,y);
else {
pii a=query(p*2,x,mid), b=query(p*2+1,mid+1,y);
return make_pair(a.first|b.first,a.second*b.second%mod);
}
}
signed main() {
n=read(), q=read();
sieve();
rep(i,1,n) a[i]=read();
build(1,1,n);
rep(i,1,q) {
char opt[10]; scanf("%s",opt);
if(opt[0]=='M') {
int l=read(), r=read(), x=read(), s=0;
rep(i,1,cnt) if(x%pr[i]==0) s+=(1ll<<i);
modify(1,l,r,x,s);
} else {
int l=read(), r=read();
pii res=query(1,l,r);
int ans=res.second, s=res.first;
rep(i,1,cnt) if(s&(1ll<<i)) ans=(ans*inv[i]%mod*(pr[i]-1)%mod);
printf("%lld\n",ans);
}
}
return 0;
}
P5568 [SDOI2008]校门外的区间
把操作全部转化为区间赋值/反转,于是就和序列操作那题一样了。
[数据隐藏]
一些有趣的线段树题需要维护两个tag互相不独立的tag。比如这题 (ZR十连测3A(博客仅自己可见))
P4314 CPU监控
First thing first,假如一个区间出现过整体赋值,再有整体加法,那么相当于第二次整体赋值;即若出现过整体赋值,那么后面的整体加法全部变为整体赋值。
第二点,把父亲的tag下放,相当于把父亲tag的操作序列接到儿子tag的操作序列后面。
这道题的tag需要维护加法tag,赋值tag,历史最大累加tag,以及历史最大赋值tag。
#include<bits/stdc++.h>
#define int long long
#define rep(i,a,b) for(register int i=(a);i<=(b);i++)
#define per(i,a,b) for(register int i=(a);i>=(b);i--)
using namespace std;
const int N=1e5+9, inf=0x3f3f3f3f3f3f3f3f;
inline int read() {
register int x=0, f=1; register char c=getchar();
while(c<'0'||c>'9') {if(c=='-') f=-1; c=getchar();}
while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+c-48,c=getchar();}
return x*f;
}
int T,a[N],E;
char opt[5];
struct node {
int l,r,m,h,t1,t2,h1,h2; //1加法 2赋值
void cov(int k,int hk) {
m=k, h=max(h,hk);
t2=k, h2=max(h2,hk);
}
void add(int k,int hk) {
if(t2!=inf) return cov(t2+k,t2+hk);
h1=max(h1,hk+t1);
t1+=k, m+=k;
h=max(h,m-t1+h1);
}
} t[N*4];
node unite(node x,node y,node r) {
r.m=max(x.m,y.m), r.h=max(x.h,y.h);
return r;
}
void build(int p,int l,int r) {
t[p].t2=inf;
int mid=((t[p].l=l)+(t[p].r=r))/2;
if(l==r) {t[p].m=t[p].h=a[l]; return;}
build(p*2,l,mid), build(p*2+1,mid+1,r);
t[p]=unite(t[p*2],t[p*2+1],t[p]);
}
void pushdown(int p) {
t[p*2].add(t[p].t1,t[p].h1), t[p*2+1].add(t[p].t1,t[p].h1);
t[p].t1=t[p].h1=0;
if(t[p].t2!=inf) {
t[p*2].cov(t[p].t2,t[p].h2), t[p*2+1].cov(t[p].t2,t[p].h2);
t[p].t2=inf;
}
}
void cov(int p,int x,int y,int k) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(x==l&&y==r) {t[p].cov(k,max(0ll,k)); return;}
pushdown(p);
if(y<=mid) cov(p*2,x,y,k);
else if(x>mid) cov(p*2+1,x,y,k);
else cov(p*2,x,mid,k), cov(p*2+1,mid+1,y,k);
t[p]=unite(t[p*2],t[p*2+1],t[p]);
}
void add(int p,int x,int y,int k) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(x==l&&y==r) {t[p].add(k,max(0ll,k)); return;}
pushdown(p);
if(y<=mid) add(p*2,x,y,k);
else if(x>mid) add(p*2+1,x,y,k);
else add(p*2,x,mid,k), add(p*2+1,mid+1,y,k);
t[p]=unite(t[p*2],t[p*2+1],t[p]);
}
node query(int p,int x,int y) {
int l=t[p].l, r=t[p].r, mid=(l+r)/2;
if(x==l&&y==r) return t[p];
pushdown(p);
if(y<=mid) return query(p*2,x,y);
else if(x>mid) return query(p*2+1,x,y);
else return unite(query(p*2,x,mid),query(p*2+1,mid+1,y),t[0]);
}
signed main() {
T=read();
rep(i,1,T) a[i]=read();
build(1,1,T);
E=read();
rep(i,1,E) {
scanf("%s",opt);
int x=read(), y=read();
if(opt[0]=='Q') printf("%lld\n",query(1,x,y).m);
else if(opt[0]=='A') printf("%lld\n",query(1,x,y).h);
else if(opt[0]=='P') add(1,x,y,read());
else cov(1,x,y,read());
}
return 0;
}