跳到主要内容

题解:P11109 [ROI 2023] 会议 (Day 2)

· 阅读需 2 分钟
Sintle
Developer

原题链接

题意简述

nn 个区间(nn 是偶数),要求选出 n2\frac n2 个区间,满足其最小点覆盖刚好是原先 nn 个区间最小点覆盖的一半(满足原先 nn 个区间最小点覆盖也是偶数)。

解题思路

妙妙题。

首先考虑我们该如何选择区间,经典对区间右端点排序并贪心选择。

注意到在选区间的过程中,两两被选择的区间 l,rl,r 之间,编号为 [l+1,r1][l+1,r-1] 的区间只要 ll 被选择了,一定不会被选择。

因此我们只要在初始的 mm 个区间中选出 m2\frac m2 个作为最后被选的区间,这 m2\frac m2 个区间各自的后继一定数量的区间一定可以被选中为 n2\frac n2 个区间之一并不影响最终选择的区间。

由于我们要取的是 n2\frac n2 个,因此这 mm 个区间中一定有 m2\frac m2 个区间满足后继可扩展的区间数量之和 >nm2>\frac{n-m}2

参考代码

#include <bits/stdc++.h>
using namespace std;

const int N = 100005;
int T , n , nxt[N];
vector <int> v , ans;

struct point{int l , r , id;} p[N];

signed main()
{
cin.tie(nullptr)->sync_with_stdio(false);
cin >> T;
while(T--)
{
cin >> n;
for(int i = 1 ; i <= n ; i++) cin >> p[i].l >> p[i].r , p[i].id = i;
sort(p + 1 , p + n + 1 , [](point x , point y){return x.r < y.r;});
int lst = 0; v.clear();
for(int i = 1 ; i <= n ; i++) if(p[lst].r < p[i].l)
{
if(lst) nxt[lst] = i;
v.emplace_back(i) , lst = i;
}
nxt[lst] = n + 1; ans.clear();
// for(auto x : v) cout << x << ' '; cout << '\n';
sort(v.begin() , v.end() , [](int x , int y){return nxt[x] - x > nxt[y] - y;});
for(int i = 0 ; i < v.size() >> 1 ; i++) ans.emplace_back(v[i]);
for(int i = 0 ; i < v.size() >> 1 ; i++)
{
if(ans.size() == n >> 1) break;
for(int j = v[i] + 1 ; j < nxt[v[i]] ; j++)
{
ans.emplace_back(j);
if(ans.size() == n >> 1) break;
}
}
for(auto x : ans) cout << p[x].id << ' '; cout << '\n';
}
return 0;
}