CF#Div2668 E - Fixed Point Removal
阿新 • • 發佈:2020-09-07
一道挺巧妙的資料結構題
連結https://codeforc.es/contest/1405/problem/E
首先一個簡單的預處理把題目中的x,y轉化成我們熟悉的[l,r]區間
不妨設f(l,r)為從l開始,到r最多可以remove掉幾個元素,發現可不可以移掉一個元素和它的位置還有值有關
我們不妨令a[i] = i - a[i],然後問題就轉化為a[i] = 0可以remove,然後可以發現對於詢問左端點l,一個位置pos有貢獻的充要條件是a[pos] >= 0 && a[pos] <= f(l,r-1)
即f(l,r) = f(l,r-1) + [a[r] >= 0 && a[pos] <= f(l,r-1)]
解釋一下這個狀態轉移方程,首先a[r]肯定要>=0,因為如果一開始就已經小於0了,那麼無論前面刪掉多少數,a[i]也不可能 = 0,至於小於等於是因為你可以在前面刪了a[r]個數時(按照定義此時a[r] == 0)先把r這個位置刪了
然後再刪剩下的數(類似反悔貪心?).
(然後我就sb地不會做了)
其實我們可以發現每新增一個數,所有的l轉移都一樣,且只有滿足f(l,x) >= a[pos]的才能轉移,於是我們可以用樹狀陣列動態地維護這個序列 f(1,r),f(2,r).......f(r,r),顯然這個序列就有單調性,於是我們可以每次二分lmax滿足f(lmax,r-1) >= a[r],然後對於1~lmax
程式碼如下
/*E. Fixed Point Removal*/ #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<vector> using namespace std; #define ll long long int read(){ char c = getchar(); int x = 0; while(c < '0' || c > '9') c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - 48,c = getchar(); return x; } const int maxn = 3e5 + 10; int a[maxn]; vector<int>L[maxn]; vector<int>id[maxn]; int ans[maxn]; #define lowbit(x) (x & (-x)) int bit[maxn]; int n,q; int ask(int x){ int ans = 0; for(; x; x -= lowbit(x)) ans += bit[x]; return ans; } void add(int x,int y){ if(x <= 0) return; for(; x <= n; x += lowbit(x)) bit[x] += y; } int main(){ n = read();q = read(); for(int i = 1; i <= n; ++i){ a[i] = i - read(); } for(int i = 1; i <= q; ++i){ int l = read(),r = read(); l = 1 + l; r = n - r; L[r].push_back(l); id[r].push_back(i); } for(int i = 1; i <= n; ++i){ if(a[i] >= 0){ int l = 1,r = i; int pos = 0; while(l <= r){ int mid = (l + r) >> 1; if(ask(mid) >= a[i]){ l = mid + 1; pos = mid; } else r = mid - 1; } if(pos != 0){ add(1,1); add(pos+1,-1); } } for(int j = 0; j < (int)L[i].size(); ++j){ int l = L[i][j]; int Id = id[i][j]; ans[Id] = ask(l); } } for(int i = 1; i <= q; ++i){ printf("%d\n",ans[i]); } return 0; }