NKOI 3726 史上最大值

P3726史上最大值
时间限制 : - MS   空间限制 : 165536 KB 
问题描述
给出一个长度为n的序列,一开始序列中每个数字都为0。现在有两种操作:
1.将区间[x,y]的数字都加上一个整数d(0<d<=10000);
2.将区间[x,y]的数字都置为0
操作共进行了m次,问操作结束后,数列中每个数字在这m次操作过程中,出现过的最大值是多少?即历史上出现过的最大值。
输入格式
第一行,一个两个整数n和m,(1<=n,m<=100000)
接下来m行,每行第一个整数为z,表示操作类型,
z=1表示1号操作,后面三个整数x,y和d
z=2表示2号操作,后面两个整数x,y
输出格式
一行,n个整数,表示数列中,每个数字历史上出现过的最大值。
样例输入 1
5 4
1 2 4 3
1 3 5 1
2 1 5
1 1 4 2
样例输出 1
2 3 4 4 1
样例输入 2
10 10
1 7 9 8
1 6 10 1
2 6 9
1 5 8 3
1 2 3 5
2 1 5
1 2 10 5
2 5 9
1 6 9 7
1 1 9 3
样例输出 2

3 8 8 8 5 10 10 10 10 6 


这是一道线段树类型的题,不过难点就在于这道题中的线段要“竖着”看

以下图为例:

1|------------------------------|10   这是要操作的区间

    2|-------|4  (加上5)

        3|------------|6  (清0)

1|--------------|5  (加上5)

             4|-----------|7   (加上2)

我们看到:进行的操作共有4次:+5   清0  +5  +2。

清0明显是一种分界线,应该前后分开求!

我们把在 某个点上进行的操作按照先后顺序排好,问题就转化为了求最大连续和,其中连续区间不含清0操作

求最大连续和我们显然就应该用线段树来求

注意清0操作的一个小技巧:因为最大连续和中不能包含清0,可以在清0操作上

赋值为-inf,这样求出来的最大连续和自然就不包括清0操作了。

另外:求完一个点,线段树向右平移一个单位,删去移除区间的操作,添加新进入区间的操作。

可以用类似于存边的方式把每个操作按照起始位置链起来,就很简单了。

#include<cstdio>
#include<iostream>
#include<vector>
#define LL long long
using namespace std;
const int maxn=100005;
LL n,m,qust[maxn],tot=1,k,a,b,d,num,p;
vector<LL>fron[maxn],bac[maxn];
struct wk{LL lm,rm,maxx,sum;}tree[maxn<<2];
inline void _read(LL &x){
    char ch=getchar(); bool mark=false;
    for(;!isdigit(ch);ch=getchar())if(ch=='-')mark=true;
    for(x=0;isdigit(ch);ch=getchar())x=x*10+ch-'0';
    if(mark)x=-x;
}
void update(LL r){
	LL ls=r<<1,rs=(r<<1)+1;
	tree[r].sum=tree[ls].sum+tree[rs].sum;
	tree[r].lm=max(tree[ls].lm,tree[ls].sum+tree[rs].lm);
	tree[r].rm=max(tree[rs].rm,tree[rs].sum+tree[ls].rm);
	tree[r].maxx=max(max(tree[ls].maxx,tree[rs].maxx),tree[ls].rm+tree[rs].lm);
}
void change(LL r,LL x,LL y){
	if(x==y){
		tree[r].maxx=tree[r].lm=tree[r].rm=tree[r].sum=d;
		return;
	}
	LL mid=(x+y)>>1,ls=r<<1,rs=(r<<1)+1;
	if(p<=mid)change(ls,x,mid);
	else change(rs,mid+1,y);
	update(r);
}
int main(){
	_read(n);_read(m);
	LL i,j;
	for(i=1;i<=m;i++){
		_read(k);_read(a);_read(b);
		if(k==1)_read(qust[i]);
		fron[a].push_back(i);
		bac[b+1].push_back(i);
		num+=qust[i];
	}
	for(i=1;i<=n;i++){
		for(j=0;j<fron[i].size();j++){
			p=fron[i][j];
			d= qust[p]?qust[p]: -num;
			change(1,1,m);
		}
		for(j=0;j<bac[i].size();j++){
			p=bac[i][j];
			d=0;
			change(1,1,m);
		}
		printf("%d ",tree[1].maxx);
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值