题解:P17600 「RiOI-8」城ケ崎 ノア
发现性质,两行相邻必有交,为什么呢?假设两行相邻没有交,我们并到一行,不劣。
所以我们的思路就是把这些区间划分到最少个没有交的集合,这个很经典了,按
之后对当前所有集合的最右端点开 set,能插入就插入,插入不了就新开一个集合。
发现不同区间右端点可以重合,所以右端点相同要按照左端点排一下,我因为这个 WA 了好几次。
怎么计算答案?发现一个集合里有
所以可能相邻的集合一定是大小为
可以参考一下样例
所以可以对大小为
你可能会问,如果能插入现有的集合,是优先插入大小为
实际上都可以,举个例子,假设一共
这个不仅证明了插入是任意的,而且还可以进一步推广得到
现在答案求出来了,还要构造方案,这个就比较简单了,实现方法就是 set 里面存一个
我的代码:
#include<bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N = 200010;
int T,n,idx;
struct node{
int l,r,id;
}a[N];
bool cmp(node x,node y){
if(x.r != y.r)return x.r < y.r;
else return x.l < y.l;
}
int ans[N];
vector<int> e[N];
multiset<pii> s1,s2;
void solve(){
idx = 0;
s1.clear() , s2.clear();
cin>>n;
for(int i = 1;i<=n;i++){
cin>>a[i].l>>a[i].r;
a[i].id = i;
}
sort(a + 1 , a + n + 1 , cmp);
for(int i = 1;i<=n;i++){
if((!s1.empty()) && (*s1.begin()).first < a[i].l){
int id = (*s1.begin()).second;
s1.erase(s1.begin());
s2.insert({a[i].r , id});
e[id].push_back(a[i].id);
}else if((!s2.empty()) && (*s2.begin()).first < a[i].l){
int id = (*s2.begin()).second;
s2.erase(s2.begin());
s2.insert({a[i].r , id});
e[id].push_back(a[i].id);
}else{
s1.insert({a[i].r , ++idx});
e[idx].push_back(a[i].id);
}
}
int h = n - s2.size() * 2,now = 1;
while(!s2.empty()){
int p = (*s2.begin()).second;
s2.erase(s2.begin());
for(auto it : e[p])ans[it] = now;
e[p].clear();
now += 2;
}
int g = s1.size(),lft = h - g;
cout<<max(0 , 2 * g - h - 1)<<"\n";
while(!s1.empty()){
int p = (*s1.begin()).second;
s1.erase(s1.begin());
for(auto it : e[p])ans[it] = now;
e[p].clear();
if(lft){
now += 2;
lft--;
}else{
now++;
}
}
for(int i = 1;i<=n;i++)cout<<ans[i]<<" ";
cout<<"\n";
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>T;
while(T--)solve();
return 0;
}