[bzoj5050][线段树]建造摩天楼

版权声明:神的bolg... https://blog.csdn.net/Rose_max/article/details/86284375

Description

属于小Q管辖的n座摩天楼从左往右排成一排,编号依次为1到n,第i座摩天楼的高度为h_i。小Q会进行m次以下两种 操作之一: 1 l
r,询问h_l+h_{l+1}+…+h_r。 2 l
r,对于第l到r的每座摩天楼i,如果上次操作结束时h_i<h_{i-1},则将第i座摩天楼再往上造一层,即h_i增加1。
你可以认为h_0=正无穷。 请写一个程序回答小Q的每个询问。注意,此题操作一和操作二弄反了

Input

第一行包含两个正整数n,m(1<=n<=100000,1<=m<=min(100000,2n)),分别表示摩天楼的数量以及操作的数量。
第二行包含n个正整数h_1,h_2,…,h_n(1<=h_i<=n),表示每座楼的高度。
接下来m行,每行三个正整数op,l,r(1<=op<=2,1<=l<=r<=n),分别表示每个操作。
因为小Q觉得错乱不齐的建筑更加美观,因此你可以认为数据是完全随机的。

Output

对于每个询问输出一行一个整数,即区间内所有摩天楼的高度之和。

Sample Input

5 8

1 3 2 2 4

1 2 4

2 2 2

2 3 3

2 4 4

1 1 3

2 1 1

2 2 2

2 3 3

Sample Output

3

3

2

2

3

3

题解

观察到这个h是随机的
于是可以大胆猜想一种关于差值的算法
一开始觉得可以分块,每个块维护块内和前面差值最小的,加就直接加然后差值变为0了就得暴力重构
然而分块这样会出现一些问题,大概是差值变为0了之后每个块的重构次数就会暴涨
然后这样就不能分析了
而且感觉代码也超级难写的样子,还要讨论块前一个的贡献
可以把分块改成线段树,同样维护区间差值最小
单考虑一个点的话,有两种东西要维护
如果他比左边的点要矮,你需要维护一个值使得加了这个值的次数后这个点就不增了
如果他比右边的点要矮,同样要维护一个值使得加了这个值的次数后右边的点就要变得增了
注意这里记录的值都是在处于左边/右边的点不动的情况下讨论的,因为动的话差值并不会缩小
于是可以直接维护了,加就直接加
每次找到差值为0的地方,把这个点拿出来暴力重构他,以及他左边的点的差值和右边点的差值
注意并不能重构左右的点,因为重构左右的点的话可能会使得左右的点下一次就不能被增加了,但是实际上是可以被增加的,因为左右的左右的点还没有把标记下传
具体的话…可以看看代码
有注释感觉挺好懂的

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
#define lc now<<1
#define rc now<<1|1
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(LL x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=100005;


int a[MAXN],n,m;
int ok[MAXN],L[MAXN],R[MAXN];//这个点是否可以增加   左边是否要注意重构   右边是否要注意重构 
bool f1(int x){return a[x-1]>a[x];}//这个点是否可以增加 
int cl(int x){return (ok[x]&&!ok[x-1])?a[x-1]-a[x]:999999999;}
int cr(int x){return (ok[x]&&!ok[x+1])?a[x+1]-a[x]:999999999;}


int list[MAXN],tp;//需要重构的点 

struct segtree
{
	LL sum[4*MAXN];int size[4*MAXN],add[4*MAXN],minl[4*MAXN],minr[4*MAXN];//有多少个可用点 
	void pushup(int now)
	{
		size[now]=size[lc]+size[rc];
		sum[now]=sum[lc]+sum[rc];
		minl[now]=min(minl[lc],minl[rc]);minr[now]=min(minr[lc],minr[rc]);
	}
	void pushdown(int now)
	{
		if(!add[now])return ;
		sum[lc]+=add[now]*size[lc];sum[rc]+=add[now]*size[rc];
		add[lc]+=add[now];add[rc]+=add[now];
		minl[lc]-=add[now];minl[rc]-=add[now];
		minr[lc]-=add[now];minr[rc]-=add[now];
		add[now]=0;
	}
	void buildtree(int now,int l,int r)
	{
		if(l==r)
		{
			size[now]=ok[l];sum[now]=a[l];
			minl[now]=L[l];minr[now]=R[l];
			return ;
		}
		int mid=(l+r)/2;
		buildtree(lc,l,mid);buildtree(rc,mid+1,r);
		pushup(now);
	}
	LL qry(int now,int l,int r,int ql,int qr)
	{
		if(l==ql&&r==qr)return sum[now];
		int mid=(l+r)/2;pushdown(now);
		if(qr<=mid)return qry(lc,l,mid,ql,qr);
		else if(mid+1<=ql)return qry(rc,mid+1,r,ql,qr);
		else return qry(lc,l,mid,ql,mid)+qry(rc,mid+1,r,mid+1,qr);
	}
	void newcal(int now,int l,int r,int p)//得到a[p]新的值
	{
		if(l==r)
		{
			if(ok[l])a[l]+=add[now];
			add[now]=0;return ;
		}
		int mid=(l+r)/2;pushdown(now);
		if(p<=mid)newcal(lc,l,mid,p);
		else newcal(rc,mid+1,r,p);
	}
	void modify(int now,int l,int r,int ql,int qr)
	{
		if(l==ql&&r==qr)
		{
			sum[now]+=size[now];add[now]++;
			minl[now]--;minr[now]--;
			return ;
		}
		int mid=(l+r)/2;pushdown(now);
		if(qr<=mid)modify(lc,l,mid,ql,qr);
		else if(mid+1<=ql)modify(rc,mid+1,r,ql,qr);
		else modify(lc,l,mid,ql,mid),modify(rc,mid+1,r,mid+1,qr);
		pushup(now);
	}
	void work(int now,int l,int r,int p)//把p的线段树节点全部更换 
	{
		if(l==r)
		{
			sum[now]=a[l];size[now]=ok[l];
			minl[now]=L[l];minr[now]=R[l];
			return ;
		}
		int mid=(l+r)/2;pushdown(now);
		if(p<=mid)work(lc,l,mid,p);
		else work(rc,mid+1,r,p);
		pushup(now);
	}
	void putl(int now,int l,int r,int p,int c)
	{
		if(l==r){minl[now]=c;return ;}
		int mid=(l+r)/2;pushdown(now);
		if(p<=mid)putl(lc,l,mid,p,c);
		else putl(rc,mid+1,r,p,c);
		minl[now]=min(minl[lc],minl[rc]);
	}
	void putr(int now,int l,int r,int p,int c)
	{
		if(l==r){minr[now]=c;return ;}
		int mid=(l+r)/2;pushdown(now);
		if(p<=mid)putr(lc,l,mid,p,c);
		else putr(rc,mid+1,r,p,c);
		minr[now]=min(minr[lc],minr[rc]);
	}
	void fix(int now,int l,int r)
	{
		if(minl[now]>0&&minr[now]>=0)return ;
		if(l==r)
		{
			if(minl[now]<=0&&list[tp]!=l)list[++tp]=l;//这个点的左边贡献需要被更改了
			if(minr[now]<0)list[++tp]=l+1;//右边的点左边贡献需要被更改了
			return ; 
		}
		int mid=(l+r)/2;pushdown(now);
		fix(lc,l,mid);fix(rc,mid+1,r);
	}
}seg;

void do1(int x){if(x>=1&&x<=n)seg.newcal(1,1,n,x);}
void do2(int x){if(x>=1&&x<=n)seg.work(1,1,n,x);}
void do3(int x){if(x>=1&&x<=n)seg.putl(1,1,n,x,cl(x));}
void do4(int x){if(x>=1&&x<=n)seg.putr(1,1,n,x,cr(x));}

void go(int x)
{
	if(x<1||x>n)return ;
	do1(x);do1(x-1);do1(x+1);
	ok[x]=f1(x);L[x]=cl(x);R[x]=cr(x);
	do2(x);do3(x+1);do4(x-1);
}

int main()
{
	n=read();m=read();a[0]=999999999;
	for(int i=1;i<=n;i++)a[i]=read(),ok[i]=f1(i);ok[n+1]=1;
	for(int i=1;i<=n;i++)L[i]=cl(i),R[i]=cr(i);
	
	seg.buildtree(1,1,n);
	while(m--)
	{
		int opt=read(),u=read(),v=read();
		if(opt==2)pr2(seg.qry(1,1,n,u,v));
		else
		{
			seg.modify(1,1,n,u,v);
			tp=0;seg.fix(1,1,n);
			for(int i=1;i<=tp;i++)go(list[i]);
			go(u);go(v+1);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Rose_max/article/details/86284375