CF739C Alyona and towers 题解
题意
给你一个数组,每次区间加后求最长单峰。
思路
考虑一个区间加的常用 trick,将数组差分,于是每次区间加只需要在差分数组上修改两处即可。
单峰的定义在差分数组上变为前一部分为正整数,后一部分为负整数的子序列。
考虑使用两个 set 维护最长单峰,第一个 set 用来维护单峰的划分情况,第二个 multiset 用来维护所有已划分出的单峰的长度。
由于差分后单峰定义只和子序列里元素的符号有关,所以只用管元素符号变化的情况,但是要注意 0 元素的特判。
省流:大分讨
当正数变负数时,若该元素不为单峰里的最后一个元素,将该元素所在的单峰按该元素的位置切成两半,若该元素为单峰里的第一个元素且前面不是 0,则将该元素合并到前面一个单峰上去,注意若该元素之后的数都为负数,则后面的数也要合并到前面去。
负数变正数时同理。
当非 0 变为 0 时,前后切割即可。
当 0 变非 0 时,左右分情况合并。
对于维护单峰长度,每次操作时将会被影响的单峰的长度在第二个 multiset 里删除,操作完后再塞回 multiset 就行。
上面的简化了很多情况写的时候还是要注意多分析。
思路还算简单,代码也许不是很易懂,反正我写的依托(总比线段树分讨好写)。
码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int MaxN=300000;
typedef multiset<int,greater<int>>::iterator Iter;
multiset<int,greater<int>>ans;
struct ODT{
ODT(int l,int r):l(l),r(r){
it=ans.insert(r-l+1);
}
ODT(const ODT&obj){
l=obj.l;
r=obj.r;
it=ans.insert(*obj.it);
}
~ODT(){
ans.erase(it);
}
int l,r;
Iter it;
bool operator<(const ODT&obj)const{return l>obj.l;}
};
multiset<ODT>tree;
int n,a[MaxN+1],m;
int arr[MaxN+1];
set<ODT>::iterator Merge(set<ODT>::iterator it,int pos,int val){
arr[pos]+=val;
int l=it->l,r=it->r;
auto p=tree.lower_bound(ODT(l-1,0));
if(p!=tree.end()&&p->r+1==l&&(arr[p->r]>0||(arr[p->r]<0&&arr[l]<0))){
int temp=p->l;
tree.erase(it);
tree.erase(p);
it=tree.emplace(temp,r);
}
l=it->l,r=it->r;
p=tree.lower_bound(ODT(r+1,0));
if(p!=tree.end()&&r==p->l-1&&(arr[r]>0||(arr[r]<0&&arr[p->l]<0))){
int temp=p->r;
tree.erase(it);
tree.erase(p);
it=tree.emplace(l,temp);
}
arr[pos]-=val;
return it;
}
void Update(int pos,int x){
if(pos<=1)return;
if(pos>n)return;
if(arr[pos]==0&&arr[pos]+x!=0){
Merge(tree.emplace(pos,pos),pos,x);
}else if(arr[pos]!=0&&arr[pos]+x==0){
auto it=tree.lower_bound(ODT(pos,0));
int l=it->l,r=it->r;
tree.erase(it);
if(arr[pos]>0){
if(l<=pos-1)Merge(tree.emplace(l,pos-1),pos,x);
if(pos+1<=r)Merge(tree.emplace(pos+1,r),pos,x);
}else{
if(l<=pos-1)Merge(tree.emplace(l,pos-1),pos,x);
if(pos+1<=r)Merge(tree.emplace(pos+1,r),pos,x);
}
}else{
auto it=tree.lower_bound(ODT(pos,0));
int l=it->l,r=it->r;
if(arr[pos]>0&&arr[pos]+x<0){
tree.erase(it);
Merge(tree.emplace(l,pos),pos,x);
if(pos+1<=r)Merge(tree.emplace(pos+1,r),pos,x);
}else if(arr[pos]<0&&arr[pos]+x>0){
tree.erase(it);
if(l<=pos-1)Merge(tree.emplace(l,pos-1),pos,x);
Merge(tree.emplace(pos,r),pos,x);
}
}
arr[pos]+=x;
}
void Solve(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
arr[1]=0;
for(int i=2;i<=n;i++)arr[i]=a[i]-a[i-1];
int last=1,p=1;
while(p<=n){
int cnt=0;
while(p<=n&&arr[p]>0)p++,cnt++;
while(p<=n&&arr[p]<0)p++;
if(last>=p){
p++;
last=p;
}else{
tree.emplace(last,p-1);
last=p;
}
}
cin>>m;
for(int i=1;i<=m;i++){
int l,r,d;
cin>>l>>r>>d;
Update(l,d);
Update(r+1,-d);
cout<<max(1ll,*ans.begin()+1)<<'\n';
}
}
#undef int
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
Solve();
return 0;
}