最nb的就是这种算法简单思维困难的题。
题目说,选最大,将左中右三个一起减少。
使用瞪眼法你要发现一个性质:
修改一个数,那左中右的相对大小不变。所以它左右的数不会被修改,永远不会。因为其它数最多会使它们减少。
也就是说,我们要维护的其实是所有会被操作的数的和。
哪些数会被操作呢。
数列问题要考虑单调区间。
对于一个单调区间,从最高的那个点开始,我们会处理第一个,第三个,第五个,,这样的点。
也就是说,对于一个区间,我们只处理奇偶性相同的一些点。
所以我们考虑维护极端点,就是那些隔开区间的点点。用set装就行。
现在我们要想,修改一个点会影响哪些区间。举个栗子。如果原来是这样
1,9,7,9,1,。
如果把7修改成10,那首先影响了[9,7]和[7,9]这两个,变成不存在的区间。
其次使得[1,9]和[9,1]扩展了。所以会影响左右各两个区间总共四个。
在计算答案的时候,我们一个一个区间地修改,我使用了左开右闭表示。但是还有些细节:
比如这两个区间长成这个样子:5,3,8;我们发现对于[3,8]这个区间,3会被4修改。但我们一段一段计算,3会被[5,3]计算进去。所以我们要判断。也就是说,如果我和我右边的奇偶性不同,且我是低点,我就要减1。
而对于最左边那个端点,因为是左开右闭,我们要先确定这个端点是否计算。如果这个端点是一个低点,且和下一个点奇偶性相同,和上一个点奇偶性也相同,它才会在本次进入计算。否则,它早就在之前被计算了。
如此,你就可以水过去一个黑题。
#include<bits/stdc++.h>
using namespace std;
#define in read()
#define IT set<int>::iterator
#define int long long
int in{
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){
ch=getchar();if(ch=='-')f=-1;
}
while(isdigit(ch)){
cnt=cnt*10+ch-48;
ch=getchar();
}return cnt*f;
}
set<int> s;int ans;
int lowbit(int u){
return u&(-u);
}
struct node{
int t[100003];
void update(int u,int key){
while(u<=100000){
t[u]+=key;u+=lowbit(u);
}
}
int query(int u,int x){
int sum=0;
while(x){
sum+=t[x];x-=lowbit(x);
}
while(u){
sum-=t[u];u-=lowbit(u);
}
return sum;
}
}b[2];int n,a[100003],q;
#define ODD(o) ((*l^*o(j=l))&1)//表示我和下一个或者上一个的奇偶性是否相同。
void calc(IT l,IT r,int inv){
IT i,j;int sum=(*l&&(a[*l]<a[*l+1])&&!ODD(--)&&!(ODD(++)))?a[*l]:0;
for(i=l++;i!=r;i=l++){
if(a[*l]>a[*i])sum+=b[*l&1].query(*i,*l);
else sum+=b[*i&1].query(*i,*l-ODD(++));
}ans+=sum*inv;
}
void yzzkal(int pos){
if((a[pos]<a[pos+1])!=(a[pos-1]<a[pos]))s.insert(pos);
else s.erase(pos);
}
void update(int key,int pos){
IT i=s.lower_bound(pos),l=i,r=i;
--l;if(l!=s.begin())--l;
++r;if(r==s.end()||(*i==pos)&&(++r==s.end()))--r;
calc(l,r,-1);
b[pos&1].update(pos,key-a[pos]);a[pos]=key;
yzzkal(pos);if(pos>1)yzzkal(pos-1);if(pos<n)yzzkal(pos+1);
calc(l,r,1);
}
signed main(){
n=in;s.insert(0);s.insert(n+1);for(int i=1;i<=n;i++)update(in,i);
//cout<<ans<<"# "<<endl;
q=in;
while(q--){
int x=in;int y=in;update(y,x);cout<<ans<<'\n';
}
return 0;
}