Description
维护一个长度为N的序列a,现在有三种操作:
1)给出参数U,V,C,将a[U],a[U+1],…,a[V-1],a[V]都赋值为C。
2)给出参数U,V,C,对于区间[U,V]里的每个数i,将a[i]赋值为max(a[i]+C,0)。
3)给出参数U,V,输出a[U],a[U+1],…,a[V-1],a[V]里值为0的数字个数。
第一行包含两个正整数N,M(1<=N,M<=300000),分别表示序列长度和操作个数。
第二行包含N个整数,其中第i个数表示a[i] (0<=a[i]<=109),描述序列的初始状态。
接下来M行描述M个操作,保证1<=U<=V<=N,对于操作1,0<=C<=109,对于操作2,|C|<=109。
Solution
模板题
操作1非常简单
操作2可以拆成两步,我们先区间加,再区间取max
操作3可以发现0一定是最小值,我们只需要统计区间最小值为0的最小值出现次数就可以了
线段树区间取max的话就是维护一个区间最小值和严格次小值
如果v小于最小值显然不用动它
如果v在最小值和次小值之间那么更改最小值并修改最小值变化产生的贡献
如果v大于等于次小值那么递归处理
时间复杂度的分析可以看课件,当然论文也是资瓷的
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
typedef long long LL;
const LL INF=(1LL<<62);
const int N=400005;
LL mn[N<<2],sec[N<<2],cnt[N<<2],tag[N<<2],cov[N<<2],w;
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void push_up(int now) {
int ls=now<<1,rs=now<<1|1;
if (mn[ls]<mn[rs]) {
mn[now]=mn[ls]; cnt[now]=cnt[ls];
sec[now]=std:: min(sec[ls],mn[rs]);
}
if (mn[rs]<mn[ls]) {
mn[now]=mn[rs]; cnt[now]=cnt[rs];
sec[now]=std:: min(sec[rs],mn[ls]);
}
if (mn[rs]==mn[ls]) {
mn[now]=mn[ls]; cnt[now]=cnt[ls]+cnt[rs];
sec[now]=std:: min(sec[ls],sec[rs]);
}
}
void push_down(int now,int tl,int tr) {
int ls=now<<1,rs=now<<1|1,mid=(tl+tr)>>1;
if (~cov[now]) {
w=cov[now]; cov[now]=-1;
cnt[ls]=mid-tl+1,cnt[rs]=tr-mid;
sec[ls]=sec[rs]=INF;
cov[ls]=cov[rs]=w;
tag[ls]=tag[rs]=0;
mn[ls]=mn[rs]=w;
}
if (tag[now]) {
w=tag[now]; tag[now]=0;
tag[ls]+=w; tag[rs]+=w;
sec[ls]+=w; sec[rs]+=w;
mn[ls]+=w; mn[rs]+=w;
}
if (mn[ls]<mn[now]) mn[ls]=mn[now];
if (mn[rs]<mn[now]) mn[rs]=mn[now];
// mn[ls]=std:: max(mn[ls],mn[now]);
// mn[rs]=std:: max(mn[rs],mn[now]);
}
void add(int now,int tl,int tr,int l,int r,int v) {
if (tl>=l&&tr<=r) {
tag[now]+=v;
sec[now]+=v;
mn[now]+=v;
return ;
}
push_down(now,tl,tr);
int mid=(tl+tr)>>1;
if (l<=mid) add(now<<1,tl,mid,l,r,v);
if (mid+1<=r) add(now<<1|1,mid+1,tr,l,r,v);
push_up(now);
}
void cover(int now,int tl,int tr,int l,int r,int c) {
if (tl>=l&&tr<=r) {
cov[now]=mn[now]=c;
cnt[now]=tr-tl+1;
sec[now]=INF;
tag[now]=0;
return ;
}
push_down(now,tl,tr);
int mid=(tl+tr)>>1;
if (l<=mid) cover(now<<1,tl,mid,l,r,c);
if (r>=mid+1) cover(now<<1|1,mid+1,tr,l,r,c);
push_up(now);
}
void upd(int now,int tl,int tr,int l,int r,int v) {
if (r<l||v<=mn[now]) return ;
if (tl>=l&&tr<=r&&v<sec[now]) {
mn[now]=v;
return ;
}
push_down(now,tl,tr);
int mid=(tl+tr)>>1;
if (l<=mid) upd(now<<1,tl,mid,l,r,v);
if (mid+1<=r) upd(now<<1|1,mid+1,tr,l,r,v);
push_up(now);
}
void build(int now,int tl,int tr) {
cov[now]=-1;
if (tl==tr) {
mn[now]=read();
sec[now]=INF;
cnt[now]=1;
return ;
}
int mid=(tl+tr)>>1;
build(now<<1,tl,mid); build(now<<1|1,mid+1,tr);
push_up(now);
}
int query(int now,int tl,int tr,int l,int r) {
if (r<l) return 0;
if (tl>=l&&tr<=r) return (!mn[now])*cnt[now];
push_down(now,tl,tr);
int mid=(tl+tr)>>1;
int qx=query(now<<1,tl,mid,l,std:: min(mid,r));
int qy=query(now<<1|1,mid+1,tr,std:: max(mid+1,l),r);
return qx+qy;
}
int main(void) {
freopen("data.in","r",stdin);
freopen("myp.out","w",stdout);
int n=read(),m=read();
build(1,1,n);
for (int opt,l,r;m--;) {
opt=read(),l=read(),r=read();
if (opt==1) cover(1,1,n,l,r,read());
else if (opt==2) {
add(1,1,n,l,r,read());
upd(1,1,n,l,r,0);
} else printf("%d\n", query(1,1,n,l,r));
}
return 0;
}