P5069 [Ynoi2015]纵使日薄西山

https://www.luogu.com.cn/problem/P5069

首先对于最大的那个数,两侧的两数要跟着他一起下降,所以它们永远不会成为最大得到,那不妨直接让这个最大的减成 (0),同时,两侧两数也就也变成 (0)
然后再去找下一个最大的
所以只要求哪些数会成为当前最大的,计算它们的和即可,可以发现是对于每一个极大的单调区间,取第 (1,3,5,7....) 大的数
然后想到这就不会维护了

然后考虑怎么维护这个东西,首先用 set 记录下每一个极大(小)值点,然后对于每个极小值点,计算周围一个单调减、一个单调增区间的贡献即可(用一奇一偶两个线段树)
需要注意一些边界问题
再考虑单调修改以后怎么做,修改一个数,对与单调性最多只会对周围两个区间产生影响
对于贡献,则要对于 在修改前的数组中,左右两侧分别第二个极值点 围城的区间中,按照修改后的数组重新计算
这两点修改的影响应该还算好理解

细节很烦,调了好久

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<set>
#include<iomanip>
#include<cstring>
#define reg register
#define EN puts("")
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=getchar();}
	return y?x:-x;
}
#define N 100006
int a[N];
struct Tree{
	struct Node{
		Node *ls,*rs;
		long long sum;
	}dizhi[N*2],*root;
	int tot;
	void build(Node *tree,int l,int r){
		if(l==r) return;
		tree->ls=&dizhi[++tot];tree->rs=&dizhi[++tot];
		int mid=(l+r)>>1;
		build(tree->ls,l,mid);build(tree->rs,mid+1,r);
	}
	void change(Node *tree,int l,int r,int pos,int k){
		if(l==r) return tree->sum=k,void();
		int mid=(l+r)>>1;
		if(pos<=mid) change(tree->ls,l,mid,pos,k);
		else change(tree->rs,mid+1,r,pos,k);
		tree->sum=tree->ls->sum+tree->rs->sum;
	}
	long long ask(Node *tree,int l,int r,int ql,int qr){
		if(ql<=l&&r<=qr) return tree->sum;
		int mid=(l+r)>>1;
		long long ret=0;
		if(ql<=mid) ret+=ask(tree->ls,l,mid,ql,qr);
		if(qr>mid) ret+=ask(tree->rs,mid+1,r,ql,qr);
		return ret;
	}
}odd,even;
int n,m;
std::set<int>set;
long long get(int x){
	if(x<1||x>n) return 0;
	std::set<int>::iterator it=set.find(x),nex,pre;
	nex=it;++nex;
	pre=it;--pre;
	//*it 应为极小值点
	if(*it==1){
		if(a[*it]<a[*nex]){
			if((*nex)&1) return odd.ask(odd.root,1,n,1,(*nex)-1);
			return even.ask(even.root,1,n,1,(*nex)-1);
		}
		return 0;
	}
	if(*it==n){
		if(a[*it]<=a[*pre]){
			if((*pre)&1) return odd.ask(odd.root,1,n,*pre,n);
			return even.ask(even.root,1,n,*pre,n);
		}
		return a[n];//前一个极小值点并不会计算到 a[n]
	}
	if(a[*it]<=a[*pre]&&a[*it]<a[*nex]){
		long long ret=0;
		if((*pre)&1) ret+=odd.ask(odd.root,1,n,*pre,(*it)-1);
		else ret+=even.ask(even.root,1,n,*pre,(*it)-1);
		if((*nex)&1) ret+=odd.ask(odd.root,1,n,*(it)+1,(*nex)-1);
		else ret+=even.ask(even.root,1,n,*(it)+1,(*nex)-1);
		if(((*it)&1)==((*nex)&1)&&((*it)&1)==((*pre)&1)) ret+=a[*it];
		return ret;
	}
	return 0;
}
int main(){
	n=read();
	odd.root=&odd.dizhi[0];even.root=&even.dizhi[0];
	odd.build(odd.root,1,n);even.build(even.root,1,n);
	for(reg int i=1;i<=n;i++){
		a[i]=read();
		if(i&1) odd.change(odd.root,1,n,i,a[i]);
		else even.change(even.root,1,n,i,a[i]);
	}
	set.insert(-2);set.insert(-1);set.insert(n+2);set.insert(n+3);
	set.insert(1);set.insert(n);
	for(reg int i=2;i<n;i++)
		if((a[i]>a[i-1]&&a[i]>=a[i+1])||(a[i]<=a[i-1]&&a[i]<a[i+1])) set.insert(i);
	long long ans=0;
	for(std::set<int>::iterator it=set.begin();it!=set.end();++it) ans+=get(*it);
	m=read();
	int x,y;
	while(m--){
		x=read();y=read();
		int nex=*set.upper_bound(x),pre=*--set.lower_bound(x);
		ans-=get(nex);ans-=get(pre);
		nex=*set.upper_bound(nex);pre=*--set.lower_bound(pre);
		ans-=get(nex);ans-=get(pre);
		if(set.find(x)!=set.end()) ans-=get(x),set.erase(x);
		set.erase(x-1);set.erase(x+1);//若 x-1 或 x+1 是极值点,则删除
		if(x&1) odd.change(odd.root,1,n,x,y);
		else even.change(even.root,1,n,x,y);
		a[x]=y;
		for(reg int i=x-1;i<=x+1;i++)if(i>=1&&i<=n){
			if(i==1||i==n) set.insert(i);
			else if((a[i]>a[i-1]&&a[i]>=a[i+1])||(a[i]<=a[i-1]&&a[i]<a[i+1])) set.insert(i);
		}
		for(std::set<int>::iterator it=set.find(pre),end=set.upper_bound(nex);it!=end;++it)
			ans+=get(*it);
		printf("%lld
",ans);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/suxxsfe/p/14147177.html