1. 程式人生 > 實用技巧 >Codeforces Round #668 (Div. 2) Eb

Codeforces Round #668 (Div. 2) Eb

E - Fixed Point Removal
每一個數字,能夠被刪除,當且僅當滿足:\(pos_i-a_i-x<=0\)其中x是他之前已經被刪掉的數字的個數。(因為我們總可以在\(pos_i-a_i-x==0\)的時候,將該數字刪除,所以小於等於0的時候一定滿足。)
\(a[i]=i-a[i]\)。(這個時候的\(a[i]\)就表示成,若他自己能被刪除的話,需要\(a[i]\)個數字,在他位置之前刪掉。)
1.當\(a[i]<0\),一定不行。
2.當\(a[i]>=0\)的時候,我們首先令\(lf_i\)表示第\(i\)個數字,能被刪除的時候,他前面最多被設定了多少個障礙(即題目輸入\(x\)

的大小)。我們假設\(lf_i=L\)。由上述表達可以發現,當\(x<=L\)(題目輸入的\(x\)),第\(i\)個能被刪除。否則不行。可以發現該\(L\)具有可以區分兩個區間的單調性。故我們想到二分。
考慮\(lf_i\)的轉移,\(lf_i=\sum_{j=1}^{i-1}{lf_j}(lf_j>=L)\)
我們就上述式子考慮離線演算法。
維護\(lf_i\)陣列,需要高效的更新和查詢,我們考慮利用主席樹。在主席書上二分出\(L\)。將\(lf_i\)扔進主席樹中,每一次查詢優先進入右子樹,否則進入左子樹,就能夠求出最大的\(L\),滿足\(a[i]\).

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<climits>
#include<stack>
#include<vector>
#include<queue>
#include<set>
#include<bitset>
#include<map>
//#include<regex>
#include<cstdio>
#include <iomanip>
#pragma GCC optimize(2)
#define up(i,a,b)  for(int i=a;i<b;i++)
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
typedef unsigned long long ull;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
ll read()
{
	char ch = getchar(); ll x = 0, f = 1;
	while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
	return x * f;
}
typedef pair<int, int> pir;
#define lson l,mid,root<<1
#define rson mid+1,r,root<<1|1
#define lrt root<<1
#define rrt root<<1|1
const int N = 3e5 + 10;
int n, q;
int a[N];
struct zxs {
	int ls[N * 40];
	int rs[N * 40];
	int sum[N * 40];
	int root[N];
	int tot = 0;
	void insert(int &o, int pre, int l, int r, int val) {
		o = ++tot;
		sum[o] = sum[pre] + 1;
		ls[o] = ls[pre]; rs[o] = rs[pre];
		if (l == r)return;
		int mid = (l + r) >> 1;
		if (val <= mid)insert(ls[o], ls[pre], l, mid, val);
		else insert(rs[o], rs[pre], mid + 1, r, val);
	}
	int query(int o, int pre, int l, int r, int k) {
		if (l == r)return l;
		int mid = (l + r) >> 1;
		if (sum[rs[o]] - sum[rs[pre]] >= k)return query(rs[o], rs[pre], mid + 1, r, k);
		else return query(ls[o], ls[pre], l, mid, k - (sum[rs[o]] - sum[rs[pre]]));
	}
	int querysum(int o, int pre, int l, int r, int lf, int rt) {
		if (lf <= l && r <= rt) {
			return sum[o] - sum[pre];
		}
		int mid = (l + r) >> 1;
		int ans = 0;
		if (lf <= mid)ans = querysum(ls[o], ls[pre], l, mid, lf, rt);
		if (rt > mid)ans += querysum(rs[o], rs[pre], mid + 1, r, lf, rt);
		return ans;
	}
}T;
vector<pir>vec[N];
int ans[N];
int main() {
	n = read(), q = read();
	upd(i, 1, n)
	{
		a[i] = read();
		a[i] = i - a[i];
	}
	int x, y;
	upd(i, 1, q) {
		x = read(), y = read();
		vec[n - y].push_back({ x,i });
	}
	upd(i, 1, n) {
		if (a[i] >= 0) {
			int temp;
			if (T.sum[T.root[i - 1]] < a[i]) {
				T.root[i] = T.root[i - 1]; goto end;
			}
			if (a[i] == 0)temp = i - 1;
			else temp = T.query(T.root[i - 1], T.root[0], 0, n - 1, a[i]);
			T.insert(T.root[i], T.root[i - 1], 0, n - 1, temp);
		}
		else T.root[i] = T.root[i - 1];
		end:
		for (auto k : vec[i]) {
			ans[k.second] = T.querysum(T.root[i], T.root[0], 0, n - 1, k.first, n - 1);
		}
	}
	upd(i, 1, q)printf("%d\n", ans[i]);
	return 0;
}