[樹狀陣列] Codeforces 1406D Three Sequences
題目大意
給定一個序列 \(a_1,a_2,\dots,a_n\),要求構造出長度為 \(n\) 的序列 \(\{b_n\},\{c_n\}\),滿足 \(a_i=b_i+c_i\),並且 \(\{b_n\}\) 是不下降序列,\(\{c_n\}\) 是不上升序列。有 \(q\) 次操作,每次操作可以把 \([l,r]\) 的數增加 \(x\),求每次操作後最小的 \(\max\{b_i,c_i\}\)。
\((n,q\leq 10^5)\)
題解
考慮構造 \(\{a_n\},\{b_n\},\{c_n\}\) 的差分序列 \(\Delta a_i=a_{i+1}-a_i,\Delta b_i=b_{i+1}-b_i,\Delta c_i=c_{i+1}-c_i\)
因為要最小化 \(\max\{b_i,c_i\}\),當 \(\Delta a_i>0\) 時,令 \(\Delta b_i=\Delta a_i,\Delta c_i=0\)。當 \(\Delta a_i\leq 0\) 時,令 \(\Delta b_i=0,\Delta c_i=\Delta a_i\)。顯然這樣是最優的,因為當 \(\Delta a_i>0\) 時,若 \(\Delta b_i<\Delta a_i\),則 \(\Delta c_i>0\)
那麼假設我們已經確定了 \(b_1\) 和 \(c_1\),則 \(b_n=b_1+\sum_{i=1}^{n-1}\Delta b_i\)。因為 \(\{b_n\}\) 不降, \(\{c_n\}\) 不升,所以 \(\max\{b_i,c_i\}=\max\{b_n,c_1\}=\max\{b_1+\sum_{i=1}^{n-1}\Delta b_i,c_1\}\)。我們肯定是希望 \(b_n\) 和 \(c_1\)
對於 \(\sum_{i=1}^{n-1}\Delta b_i\),我們可以通過預處理求出,對於將區間 \([l,r]\) 加上 \(x\),因為我們做了差分,只需要修改左右兩個端點對 \(\Delta b_i\) 造成的影響即可,然後還要求每次修改後的 \(\sum_{i=1}^{n-1}\Delta b_i\),於是可以使用樹狀陣列來維護,時間複雜度 \(O(n \log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define RG register int
#define LL long long
template<typename elemType>
inline void Read(elemType &T){
elemType X=0,w=0; char ch=0;
while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
T=(w?-X:X);
}
inline int lowbit(int x){return x&(-x);}
LL a[100010],Delta[100010];
struct BIT{
LL Node[100010];
int N;
void set_Len(int len){N=len;}
void Update(int x,LL Add){for(;x<=N;x+=lowbit(x)) Node[x]+=Add;}
LL PrefixSum(int x){LL Res=0;for(;x;x-=lowbit(x)) Res+=Node[x];return Res;}
LL Query(int L,int R){return PrefixSum(R)-PrefixSum(L-1);}
LL GetAns(){
LL Res=a[1]+PrefixSum(N);
return (Res&1)?((Res>>1)+1):(Res>>1);
}
};
BIT Tree;
int N,M;
int main(){
Read(N);
Tree.set_Len(N-1);
for(RG i=1;i<=N;++i)
Read(a[i]);
for(RG i=1;i<N;++i){
if(a[i+1]-a[i]>0) Tree.Update(i,a[i+1]-a[i]);
Delta[i]=a[i+1]-a[i];
}
printf("%I64d\n",Tree.GetAns());
Read(M);
while(M--){
int L,R;LL Add;
Read(L);Read(R);Read(Add);
if(L==1) a[1]+=Add;
if(L>1){
Delta[L-1]+=Add;
Tree.Update(L-1,-Tree.Query(L-1,L-1));
if(Delta[L-1]>0) Tree.Update(L-1,Delta[L-1]);
}
if(R<N){
Delta[R]-=Add;
Tree.Update(R,-Tree.Query(R,R));
if(Delta[R]>0) Tree.Update(R,Delta[R]);
}
printf("%I64d\n",Tree.GetAns());
}
return 0;
}