CF1405E. Fixed Point Removal

\(\texttt{Difficulty:2300}\)

题意

一个长为 \(n(1\le n\le3\cdot10^5)\) 的序列 \(a(1\le a_i\le n)\) ,每次操作可以选择一个 \(a_i=i\) 的数删去,后面的元素整体前移一个位置。 \(q(1\le q\le3\cdot10^5)\) 次询问,每次询问 \(l,r\) ,求在区间 \([1,l]\)\([r,n]\) 被禁止删除的情况下,最多能删除多少次。

思路

考虑令 \(a_i=i-a_i\) ,于是可以发现当前可以删除的数字就是 \(a_i=0\) 的位置,并且删除后其后面所有位置的值 \(-1\) ,显然 \(a_i\) 为负数则永远不可能被删除。考虑将询问离线,对询问按右端点排序,之后维护序列 \(f\)\(f_i\) 表示从 \(i\) 开始到当前所能删除的总个数,由于一个数字想要被删除,其前面能被删除的数的个数至少为 \(a_i\) ,满足这个条件的话我们每次删掉最右边的 \(a_i=0\) 的数一定可以删完所有这样的数。又因为 \(f\) 显然是单调递减的,所以当前位置只会对 \(f\) 的一个前缀产生 \(1\) 的贡献。为了方便更新贡献,我们改为维护 \(f\) 的差分数组 \(d\) ,这样对于前缀 \([1,k]\) ,只需要 \(d_1+1,d_{k+1}-1\) 即可,又因为每次都在 \(d_1\)\(+1\) ,所以只需维护 \(-1\) 的位置即可。这部分可以用树状数组来维护,之后我们需要快速找出 \(k+1\) 的位置,只需找到最后一个 \(f_i>=a_i\)\(i\) 即可,这部分可以用树状数组上二分来解决,每个询问的右端点处的 \(f_l\) 即为答案,复杂度 \(O(nlogn)\)

代码

#include<bits/stdc++.h>
#include<unordered_map>
#include<unordered_set>
using namespace std;
using LL = long long;
using LD = long double;
using ULL = unsigned long long;
using PII = pair<LL, LL>;
using TP = tuple<int, int, int>;
#define all(x) x.begin(),x.end()
#define mst(x,v) memset(x,v,sizeof(x))
#define mul(x,y) (1ll*(x)*(y)%mod)
#define mk make_pair
//#define int LL
//#define double LD
#define lc tr[x].ch[0]
#define rc tr[x].ch[1]
#define endl '\n'
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#pragma warning(disable : 4996)
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
const double eps = 1e-10;
const double pi = acos(-1);
const LL MOD = 1000000007;
const LL mod = 998244353;
const int maxn = 300010;

int N, Q, A[maxn], ans[maxn], d[maxn];
struct Query {
	int l, r, id;
}q[maxn];

void add(int i, int x)
{
	while (i <= N)
		d[i] += x, i += i & (-i);
}

int sum(int i)
{
	int ret = 0;
	while (i)
		ret += d[i], i -= i & (-i);
	return ret;
}

int kth(int k)
{
	int x = 0;
	for (int i = 19; i >= 0; i--)
	{
		int t = 1 << i;
		if (x + t <= N && k >= d[x + t])
			k -= d[x + t], x += t;
	}
	return x + 1;
}

bool cmp(const Query& a, const Query& b)
{
	return a.r < b.r;
}

void solve()
{
	int now = 0;
	sort(q + 1, q + Q + 1, cmp);
	for (int i = 1; i <= Q; i++)
	{
		while (now < q[i].r)
		{
			int k;
			if (A[now + 1] >= 0)
				k = kth(now - A[now + 1]);
			else
				k = 1;
			k = min(now + 2, k);
			add(k, 1), now++;
		}
		ans[q[i].id] = q[i].r - sum(q[i].l);
	}
	for (int i = 1; i <= Q; i++)
		cout << ans[i] << endl;
}

int main()
{
	IOS;
	cin >> N >> Q;
	for (int i = 1; i <= N; i++)
		cin >> A[i], A[i] = i - A[i];
	for (int i = 1; i <= Q; i++)
		cin >> q[i].l >> q[i].r, q[i].id = i, q[i].r = N - q[i].r, q[i].l++;
	solve();

	return 0;
}
posted @ 2022-08-15 11:27  Prgl  阅读(50)  评论(0)    收藏  举报