bzoj4355 Play with sequence 线段树区间最值操作

90 篇文章 0 订阅

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值