有一些形如[L, R]的区间,你要选出尽可能多的区间,并满足区间两两交集为空(注意[X, X]非空) 。
求最多能选出的区间数及字典序最小的方案。
如果不要求方案,这就是一道水题。
但是要求方案这就是一道火题辣。
首先我们引入一个定理
任何不影响贪心策略的线段都存在于某一最优方法中。(这里的贪心策略指的是求最多能选出的区间数的贪心策略)
如图,线段 DC,EF D C , E F 都可以选择,因为它们既不与 AB A B 相交,又不影响下一步决策(选择 GH G H )
根据这个定理,这道题的做法就是按字典序选出可行的线段。
如何运用这个定理判断此线段是否能选择?
既然它满足贪心策略,那么就用贪心来检验好了。
设 f(l,r) f ( l , r ) 为只选择 ∈[l,r] ∈ [ l , r ] 线段的答案。那么线段 [x,y] [ x , y ] 能选择当且仅当
算法流程:
#include
#include
#include
using namespace std;
const int maxn = 200005;
const int Inf = 1 << 30;
int n, l[maxn], r[maxn], tot, cnt, f[maxn * 2][20];
int *q[maxn * 2];
int lmax[maxn * 2], rmax[maxn * 2], lsum[maxn * 2], rsum[maxn * 2];
int ans[maxn];
inline bool cmp(const int* a, const int* b)
{
return *a < *b;
}
int solve(int l, int r)
{
int ans = 0;
for(int j = 18; j >= 0; --j)
if(f[l][j] <= r) ans += (1 << j), l = f[l][j] + 1;
return ans;
}
#define lowbit(x) ((x) & (-x))
void add_sum(int *p, int x)
{
while(x <= cnt) {
++p[x];
x += lowbit(x);
}
}
int query_sum(int *p, int x)
{
int ret = 0;
while(x > 0) {
ret += p[x];
x -= lowbit(x);
}
return ret;
}
void add_max(int *p, int x)
{
int t = x;
while(x <= cnt) {
p[x] = max(p[x], t);
x += lowbit(x);
}
}
int query_max(int *p, int x)
{
int ret = 0;
while(x > 0) {
ret = max(ret, p[x]);
x -= lowbit(x);
}
return ret;
}
int main()
{
//读入
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%d%d", &l[i], &r[i]), q[++tot] = &l[i], q[++tot] = &r[i];
//离散化
sort(q + 1, q + tot + 1, cmp);
for(int tmp = -1, i = 1; i <= tot; ++i) {
if(*q[i] != tmp) {
tmp = *q[i]; *q[i] = ++cnt;
}else *q[i] = cnt;
}
//构建倍增数组
for(int i = 1; i <= cnt + 1; ++i)
for(int j = 0; j <= 20; ++j)
f[i][j] = Inf;
for(int i = 1; i <= n; ++i)
f[l[i]][0] = min(f[l[i]][0], r[i]);
for(int i = cnt; i >= 1; --i) {
f[i][0] = min(f[i][0], f[i + 1][0]);
for(int j = 1; f[i][j - 1] <= cnt && (1 << j) <= n; ++j)
f[i][j] = f[f[i][j - 1] + 1][j - 1];
}
//统计答案
tot = 0;
for(int i = 1; i <= n; ++i) {
int v = tot - query_sum(rsum, l[i] - 1) - query_sum(lsum, cnt - r[i]);
if(v > 0) continue; //此区间已被覆盖
int l_lst = query_max(rmax, l[i] - 1), r_nxt = cnt - query_max(lmax, cnt - r[i]);
if(solve(l_lst + 1, l[i] - 1) + solve(r[i] + 1, r_nxt) + 1 == solve(l_lst + 1, r_nxt)) {
ans[++tot] = i;
add_sum(rsum, r[i]); add_sum(lsum, cnt - l[i] + 1);
add_max(rmax, r[i]); add_max(lmax, cnt - l[i] + 1);
}
}
//输出
printf("%d\n", tot);
for(int i = 1; i <= tot; ++i)
printf("%d ", ans[i]);
return 0;
}