CF1610C - Keshi Is Throwing a Party

\(f[j]\) 中的 \(j\) 表示你所选出的数列的已知最大长度限制,而且是只管右限制的最大长度限制。

比如说第一个数右限制是 \(4\),那么最大长度限制是 \(5\),因为要算上它本身和它右边最多可能的人的个数。

然后如果第二个数是 \(9\),那么最大限制依然是 \(5\)

如果第二个数是 \(1\),那么最大长度限制缩小为 \(3\),因为要算上这两个和它右边最多可能多出来的 \(1\) 个人。

\(f[j]\) 表示在上述条件下的最大答案。

这样子我们从左往右扫一遍,每次去更新答案,可以发现每次更新的一定是一个区间。以下是原因:

  • \(f[j]\) 是单调递减的。
  • \(j-f[j]\) 是单调递增的。
  • \(j - f[j] - 1 ≤ \text{第 i 个人的右限制}\) 时,这个人的右限制满足加入 \(f[j]\) 的条件。
  • \(f[j] ≤ \text{第 i 个人的左限制}\) 时,这个人的左限制满足加入 \(f[j]\) 的条件。

因此只需要二分求出每次刚好满足右限制和左限制的位置,卡出来的是一个区间。\(f[j]\) 用树状数组维护。

#include <cstdio>
#include <cstring>
#include <iostream>
#define lowbit(i) (i&-i)
using namespace std;
const int N = 2e5 + 1;

int t, n, a, b, tree[N ], ans, l,  r。

void add(int i, int x){
	i = n - i;
	while(i <= n){
		tree[i] += x;
		i += lowbit(i);
	}
}

int query(int i){
	i = n - i;
	int ret = 0;
	while(i > 0){
		ret += tree[i];
		i -= lowbit(i);
	}
	return ret;
}

int main(){
	scanf("%d", &t);
	while(t--){
		ans = 0;
		memset(tree + 1, 0, sizeof(int) * n);
		scanf("%d", &n);
		for(int i = 1; i <= n; i++){
			scanf("%d%d", &a, &b);
			int ans1, ans2;
			l = 0, r = n - 1;
			while(l < r){
				ans1 = (l + r + 1) >> 1;
				if(ans1 - query(ans1) <= a) l = ans1;
				else r = ans1 - 1;
			}
			ans1 = l;
			l = 0, r = n;
			while(l < r){
				ans2 = (l + r) >> 1;
				if(query(ans2) <= b) r = ans2;
				else l = ans2 + 1;
			}
			ans2 = l;
			if(ans2 <= ans1){
				add(ans1, 1);
				add(ans2 - 1, -1);
			}
		}
		for(int i = 0; i < n; i++)
			ans = max(ans, min(i + 1, query(i)));
		printf("%d\n", ans);
	}
	return 0;
}
posted @ 2021-11-25 19:44  SpaceJellyfish  阅读(112)  评论(0)    收藏  举报