题目传送门:
Problem - 1610C - Codeforces
设 中的 表示你所选出的数列的已知最大长度限制,而且是只管右限制的最大长度限制。
比如说第一个数右限制是 4,那么最大长度限制是 5,因为要算上它本身和它右边最多可能的人的个数。
然后如果第二个数是 9,那么最大限制依然是 5。
如果第二个数是 1,那么最大长度限制缩小为 3,因为要算上这两个和它右边最多可能多出来的 1 个人。
表示在上述条件下的最大答案。
这样子我们从左往右扫一遍,每次去更新答案,可以发现每次更新的一定是一个区间。以下是原因:
- 是单调递减的。
- 是单调递增的。
- 当 第 i 个人的右限制 时,这个人的右限制满足加入 的条件。
- 当 第 i 个人的左限制 时,这个人的左限制满足加入 的条件。
因此只需要二分求出每次刚好满足右限制和左限制的位置,卡出来的是一个区间。 用树状数组维护。
#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;
}