Codeforces Round #638 (Div. 2) F. Phoenix and Memory 区间贪心+线段树
题意
有n个人,标号为1--n,他们站的顺序未知,已知每个位置可能的标号范围为[a,b],要求还原朋友的站位顺序,同时,需要考虑还原后的站位是否是唯一的,如果不唯一,随意输出两种合法顺序
分析
还原顺序不难,其实是一个看起来很熟悉的问题。记录每个点开始的右边界,维护当前可选的点以及该右边界,从左往右扫,每次取最小的右边界,即可贪心得出合法的序列。该题的难点关键在于判断是否唯一。
设编号为i的人的位置为(pos_i),那么问题就是存不存在(L_{pos_j}<=pos_i<pos_j<=R_{pos_i})如果存在那么他们两个就可以互相交换了。我们考虑最暴力的做法,就是对于每个人i来说找到一个j满足以上条件,复杂度(O(n^2))。我们进一步思考,对于第i个人来说,要找到([1,n])里面有一个j满足以上不等式(注意,这里i,j不一定对应不等式里面的i,j它们可以互换例如(pos_i>pos_j)但是按照逻辑这个不等式仍然成立只是换了i,j),而由于(R[pos[i]])已经确定了,也就是找到人的标号在([1,R[pos[i]]])里面满足条件的j,由于是一对一对找,我们可以直接找([i+1,R[pos[i]])标号的人,对于该区间内的标号,pos[i]的人的标号可以变成这个区间里面的人的标号,因为i所在位置的L最小都是i,所以毋庸置疑可以变,而如果这个区间里面的最小(L)是小于等于i的,代表这个区间里面最小的L的位置的标号可以变成i位置的标号,这就代表了两两可以互换。(这里要是细看细节还是挺绕的。。)
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
#define F first
#define S second
#define mkp make_pair
#define pii pair<int,int>
typedef long long ll;
const int inf=0x3f3f3f3f;
const int maxn=2e5+10;
int tree[maxn<<2],id[maxn<<2];
int ans[maxn],pos[maxn],L[maxn],R[maxn];
int n;
vector<pair<int,int> >v[maxn];
void build(int o,int l,int r){
if(l==r){
tree[o]=L[pos[l]];
id[o]=l;
return ;
}
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
if(tree[o<<1]<tree[o<<1|1])id[o]=id[o<<1];
else id[o]=id[o<<1|1];
tree[o]=min(tree[o<<1|1],tree[o<<1]);
}
pair<int,int> query(int o,int l,int r,int x,int y){
if(x<=l&&y>=r)return mkp(tree[o],id[o]);
int mid=l+r>>1;
pair<int,int>tmp1=mkp(-1,-1),tmp2=mkp(-1,-1);
if(mid>=x)tmp1=query(o<<1,l,mid,x,y);
if(mid<y)tmp2=query(o<<1|1,mid+1,r,x,y);
if(tmp1.first==-1)return tmp2;
if(tmp2.first==-1)return tmp1;
if(tmp1.first>tmp2.first)return tmp2;
else return tmp1;
}
void print(){
for(int i=1;i<=n;i++){
cout<<ans[i]<<" ";
}
cout<<endl;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&L[i],&R[i]);
v[L[i]].pb(mkp(R[i],i));
}
set<pair<int,int> >s;
for(int i=1;i<=n;i++){
s.insert(v[i].begin(),v[i].end());
ans[(*s.begin()).second]=i;
pos[i]=(*s.begin()).second;
s.erase(s.begin());
}
build(1,1,n);
for(int i=1;i<=n;i++){
pair<int,int>tmp=query(1,1,n,i+1,R[pos[i]]);
if(i+1>R[pos[i]])continue;
if(tmp.first<=i){
//cout<<tmp.first<<" "<<tmp.second<<endl;
printf("NO
");
print();
swap(ans[pos[tmp.second]],ans[pos[i]]);
print();
return 0;
}
}
printf("YES
");
print();
}