线段树难题--史上最大值nkoj3726



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)


对于x=4我们看到:进行的操作共有4次:1. +5 2.清0     3. +5      4.   +2。

      如何处理?清0明显是一种分界线,应该前后分开求!

     仔细想一下:我们把在 某个点上进行的操作按照先后顺序排好

               问题就转化为了求最大连续和其中连续区间不含清0操作

    最大连续和:线段树!!!

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

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

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

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

详情请参见代码!!!

举个例子:图中为x=4的情况



           如有问题,请联系1879570236.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib> 
#include<algorithm>
#define inf 88888888
using namespace std;
const long long maxn=100005;
long long triplemax(long long a,long long b,long long c){
	return max(a,max(b,c));
}
struct node{
	long long a,b;
	long long l1,r1,mid1,max1;
	long long sum;
}; 
node tree[maxn*4];
long long a[maxn];
long long n,m;
void update(long long p){
	long long l=(p<<1),r=(p<<1)+1;
	tree[p].sum=tree[l].sum+tree[r].sum;
	tree[p].l1=max(tree[l].l1,tree[l].sum+tree[r].l1);
	tree[p].r1=max(tree[r].r1,tree[r].sum+tree[l].r1);
	tree[p].max1=triplemax(tree[l].max1,tree[r].max1,tree[l].r1+tree[r].l1);
}
void build_tree(long long p,long long x,long long y){
	tree[p].a=x;tree[p].b=y;
	if(x<y){
		build_tree(p<<1,x,(x+y)>>1);
		build_tree((p<<1)+1,((x+y)>>1)+1,y);
	}
}
void add(long long p,long long k,long long d){
	if(tree[p].a>k||tree[p].b<k)return;
	if(tree[p].a==tree[p].b){
		tree[p].l1=d;
		tree[p].r1=d;
		tree[p].mid1=d;
		tree[p].max1=d;
		tree[p].sum=d;
		return;
	}
	if(tree[(p<<1)].a<=k&&k<=tree[(p<<1)].b){
		add((p<<1),k,d);
	}
	if(tree[(p<<1)+1].a<=k&&k<=tree[(p<<1)+1].b){
		add((p<<1)+1,k,d);
	}
	update(p);
}
long long getsum(long long p,long long x,long long y){
	if(tree[p].b<x||tree[p].a>y)return 0;
	if(tree[p].b<=y&&tree[p].a>=x)return tree[p].sum;
	else{
		long long total=0;
		total+=getsum((p<<1),x,y);
		total+=getsum(((p<<1)+1),x,y);
		return total;
	}
}
long long lastl[100005],lastr[100005],nextl[100005],nextr[100005];
struct node2{
	long long l,r,d,id;
};
node2 change[100005];
long long ans[100005];
int main(){
	//freopen("data.in","r",stdin);
	//freopen("std1.out","w",stdout);
	long long i,j,k;
	cin>>n>>m;
	build_tree(1,1,m);
	for(i=1;i<=m;i++){
		long long z,x,y,dd;
		scanf("%I64d%I64d%I64d",&z,&x,&y);
		if(z==2)dd=-inf;
		else scanf("%I64d",&dd);
		change[i].l=x;
		change[i].r=y;
		change[i].d=dd;
		change[i].id=i;
		nextl[i]=lastl[x];
		lastl[x]=i;
		nextr[i]=lastr[y];
		lastr[y]=i;
	}
	for(i=1;i<=n;i++){
		for(long long h=lastl[i];h;h=nextl[h]){
			add(1,change[h].id,change[h].d);
		}
		ans[i]=tree[1].max1;
		for(long long h=lastr[i];h;h=nextr[h]){
			add(1,change[h].id,0);
		}
	}
	for(i=1;i<=n;i++){
		printf("%I64d ",ans[i]);
	}
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值