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]);
}
}