1. 程式人生 > 實用技巧 >CF#Div2668 E - Fixed Point Removal

CF#Div2668 E - Fixed Point Removal

一道挺巧妙的資料結構題

連結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

區間加1,把所有詢問以右端點為下標存在一個vector裡,每次處理到一個右端點就把所有詢問都拿出來在樹狀陣列上查詢一遍

程式碼如下

/*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;
}