CodeForces 1404C - Fixed Point Removal
這是我所做不出來的題目。我什麼時候才能 GM 啊?讓我回個 IM 也好嘛!
洛谷題目頁面傳送門 & CF 題目頁面傳送門
題意見洛谷。
首先探索一個下標序列能夠全部被刪除的充要條件。我們設 \(b_i=i-a_i\),那麼任意時刻,顯然 \(a_i\) 能被刪掉當且僅當 \(b_i=0\)。於是我們盯著這個 \(b\) 看:一開始肯定要有一個先驅者 \(b_i=0\) 對不對,然後刪除之後就可以讓後面的 \(b\) 值都減去 \(1\)。一個 \(b_i\),如果它 \(<0\) 那顯然沒救了;如果 \(\geq 0\) 那麼只需要將它減去 \(b_i\) 次 \(1\) 就變成 \(0\)
然後隨便 van van 發現這個也滿足充分性的,只需要構造出一個方案即可證明。那麼方案很好構造,就每次刪掉 \(c\) 中最右邊滿足 \(b_{c_i}=0\) 的 \(c_i\) 即可。
然後再對詢問進行一番顯然的轉化:每組詢問 \(x,y\) 相當於求 \(b\) 上的區間 \([l=x+1,r=n-y]\) 的最長能被 \([0,1,2,3,\cdots]\)
如何求呢??
暫時想不到直接對區間維護的,不妨假設區間左端點固定,來求(這個其實是老套路了)。那麼可以維護以每個位置為最後一位的最長子序列長度 \(-1\),這是一個 \(\mathrm O(n)\) 的數列對吧。如何求這個數列很容易,類似 DP 的,當前位置 \(i\) 答案為 \(x\) 要滿足兩個條件:
- 前面有 \(x-1\) 長度的,即 \(\max\limits_{j=1}^{i-1}\{dp_j\}\geq x-1\);
- 當前這位要滿足,即 \(x\geq a_i\)。
綜合起來就是 \(x\in\left[a_i,\max\limits_{j=1}^{i-1}\{dp_j\}+1\right]\)
於是考慮從右往左列舉左端點,相當於依次 push_front 然後維護整個 DP 陣列(跟上面套路配套的套路)。考慮如何維護。
顯然任意時刻 DP 陣列是在 \([0,1,2,3,\cdots]\) 裡面插若干個 \(-\infty\) 這個形式。然後發現在往前面 push 的過程中,push 的是 \(0\),所以這個自然數序列要往後順移,即全體加 \(1\)。然後所有 \(-\infty\) 當年所對應的區間也在加 \(1\),慢慢加的話可能有朝一日能使那個區間不為空,重獲新生。又顯然最多會有 \(n\) 次重獲新生的說,所以這個就暴力修改就可以了(每個時刻從前往後依次修改,每次修改還要附帶字尾加 \(1\))。那麼如何每次快速找到復活的人們呢?而且不僅要找到,還要每次找到的是最前面的?維護一個線段樹,然後線段樹二分即可。
最後,這個「老套路」離線 + 線段樹實現是比較方便的,但是注意到每次是在前一個歷史版本上直接修改,也可以用主席樹預處理來實現回答詢問的線上,不過我不會,而且也很簡單的樣子,不管了。
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int inf=0x3f3f3f3f;
const int N=300000,QU=N;
int n,qu;
int a[N+1];
struct query{int l,r;}qry[QU+1];
vector<int> pos[N+1];
int ans[QU+1];
struct segtree{
struct node{int l,r,dif,rl,lz;}nd[N<<2];
#define l(p) nd[p].l
#define r(p) nd[p].r
#define dif(p) nd[p].dif
#define rl(p) nd[p].rl
#define lz(p) nd[p].lz
void bld(int l=1,int r=n,int p=1){
l(p)=l;r(p)=r;dif(p)=rl(p)=-inf;lz(p)=0;
if(l==r)return;
int mid=l+r>>1;
bld(l,mid,p<<1);bld(mid+1,r,p<<1|1);
}
void init(){bld();}
void sprup(int p){dif(p)=max(dif(p<<1),dif(p<<1|1));rl(p)=max(rl(p<<1),rl(p<<1|1));}
void sprdwn(int p){
if(lz(p)){
dif(p<<1)+=lz(p);rl(p<<1)+=lz(p);lz(p<<1)+=lz(p);
dif(p<<1|1)+=lz(p);rl(p<<1|1)+=lz(p);lz(p<<1|1)+=lz(p);
lz(p)=0;
}
}
void chg(int x,int v1,int v2,int p=1){
if(l(p)==r(p))return dif(p)=v1,rl(p)=v2,void();
sprdwn(p);
int mid=l(p)+r(p)>>1;
chg(x,v1,v2,p<<1|(x>mid));
sprup(p);
}
void add(int l,int r,int v,int p=1){
if(l>r)return;
if(l<=l(p)&&r>=r(p))return dif(p)+=v,rl(p)+=v,lz(p)+=v,void();
sprdwn(p);
int mid=l(p)+r(p)>>1;
if(l<=mid)add(l,r,v,p<<1);
if(r>mid)add(l,r,v,p<<1|1);
sprup(p);
}
int mx(int l,int r,int p=1){
if(l<=l(p)&&r>=r(p))return rl(p);
sprdwn(p);
int mid=l(p)+r(p)>>1,res=-inf;
if(l<=mid)res=max(res,mx(l,r,p<<1));
if(r>mid)res=max(res,mx(l,r,p<<1|1));
return res;
}
int fst(){
int p=1;
if(dif(p)<0)return 0;
while(l(p)<r(p)){
sprdwn(p);
if(dif(p<<1)>=0)p=p<<1;
else p=p<<1|1;
}
return l(p);
}
}segt;
int main(){
cin>>n>>qu;
for(int i=1;i<=n;i++)scanf("%d",a+i),a[i]=i-a[i];
for(int i=1;i<=qu;i++)scanf("%d%d",&qry[i].l,&qry[i].r),qry[i].l++,qry[i].r=n-qry[i].r,pos[qry[i].l].pb(i);
segt.init();
for(int i=n;i;i--){
if(a[i]==0){
segt.chg(i,-inf,0);segt.add(i+1,n,1);
int fd;
while(fd=segt.fst()){
segt.chg(fd,-inf,segt.mx(1,fd-1)+1);segt.add(fd+1,n,1);
}
}
else if(a[i]>0)segt.chg(i,-a[i],-inf);
for(int j=0;j<pos[i].size();j++)ans[pos[i][j]]=max(0,segt.mx(1,qry[pos[i][j]].r)+1);
}
for(int i=1;i<=qu;i++)printf("%d\n",ans[i]);
return 0;
}