Codeforces Round #638 (Div. 2) F. Phoenix and Memory 区间贪心+线段树

Posted ttttttttrx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #638 (Div. 2) F. Phoenix and Memory 区间贪心+线段树相关的知识,希望对你有一定的参考价值。

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();
	
}

以上是关于Codeforces Round #638 (Div. 2) F. Phoenix and Memory 区间贪心+线段树的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #638 (Div. 2)

Codeforces Round #638 (Div. 2)

Codeforces Round #638 (Div. 2)

Codeforces Round #638 (Div. 2)(A~B)

Codeforces Round #638 (Div. 2) A~C题解

CF A. Phoenix and Balance Codeforces Round #638 (Div. 2) 5月1号