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\n",ans);
}
return 0;
}