Codeforces Round 638 (Div. 2)___F. Phoenix and Memory —— 贪心 +线段树

题目链接:点我啊╭(╯^╰)╮

题目大意:

     n n n 个人标号为 1 1 1 ~ n n n,顺序被打乱后
    第 i i i 个人的标号在 [ L i , R i ] [L_i, R_i] [Li,Ri] 之间
    保证答案存在,若答案唯一,则输出唯一答案
    若不唯一,则输出任意两种答案

解题思路:

    考虑先弄出一组答案:
    按照 L i L_i Li 排序,枚举加入 L i L_i Li 后,当前肯定选择 R i R_i Ri 最小的那个人
    这样肯定是最贪心的,用一个 s e t set set 维护即可


    记标号为 i i i 的人打乱后位置在 p o s i pos_i posi
    考虑什么情况下两个人的标号可以交换:
     L [ p o s i ] ≤ i ≤ R [ p o s i ] L[pos_i] ≤ i ≤ R[pos_i] L[posi]iR[posi]
     L [ p o s j ] ≤ j ≤ R [ p o s j ] L[pos_j] ≤ j ≤ R[pos_j] L[posj]jR[posj]

    假设 i < j i < j ij,要满足 i i i j j j 可以交换
    必须满足: L [ p o s j ] ≤ i < j ≤ R [ p o s i ] L[pos_j] ≤ i <j ≤ R[pos_i] L[posj]ijR[posi]
    也就是枚举 i i i,只需要找到一个 j j j 满足上式即可

    由于 R [ p o s i ] R[pos_i] R[posi] 已知,也就是在标号为 [ i + 1 , R [ p o s i ] ] [i+1, R[pos_i]] [i+1,R[posi]] 里找到一个 j j j
    其 L [ p o s j ] L[pos_j] L[posj] 最小,若 L [ p o s j ] ≤ i L[pos_j] ≤ i L[posj]i,则满足交换,这部分可以用线段树实现
    注意 i > j i > j ij 的情况是一样的,但是枚举 i < j i < j ij 就包括所有情况了
     i i i p o s i pos_i posi 绕来绕去要分清楚

#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 2e5 + 5;
int n, pos[maxn], L[maxn], R[maxn], ans[maxn];
int t[maxn<<2], id[maxn<<2];
vector <pii> v[maxn]; 
set <pii> s;
 
void build(int l, int r, int rt){
	if(l == r){
		t[rt] = L[pos[l]];
		id[rt] = l;
		return;
	}
	int mid = l + r >> 1;
	build(l, mid, rt<<1);
	build(mid+1, r, rt<<1|1);
	if(t[rt<<1] < t[rt<<1|1]) t[rt] = t[rt<<1], id[rt] = id[rt<<1];
	else t[rt] = t[rt<<1|1], id[rt] = id[rt<<1|1];
}
 
pii query(int ql, int qr, int l, int r, int rt){
	if(r<ql || l>qr) return {1e9, 0};
	if(l>=ql && r<=qr) return {t[rt], id[rt]};
	int mid = l + r >> 1; pii ret = {1e9, 0};
	ret = min(ret, query(ql, qr, l, mid, rt<<1));
	ret = min(ret, query(ql, qr, mid+1, r, rt<<1|1));
	return ret;
}
 
signed main() {
	scanf("%d", &n);
	for(int i=1; i<=n; i++){
		scanf("%d%d", L+i, R+i);
		v[L[i]].push_back({R[i], i});
	}
	for(int i=1; i<=n; i++){
		s.insert(v[i].begin(), v[i].end());
		pos[i] = (*s.begin()).second;
		ans[pos[i]] = i;
		s.erase(s.begin());
	}
	build(1, n, 1); 
	for(int i=1; i<=n; i++){
		if(i + 1 > R[pos[i]]) continue;
		pii q = query(i+1, R[pos[i]], 1, n, 1);
		int q1 = q.first, q2 = q.second;
		if(q1 == 1e9 || q1 > i) continue;
		puts("NO");
		for(int i=1; i<=n; i++) printf("%d ", ans[i]);
		puts(""); swap(ans[pos[i]], ans[pos[q2]]);
		for(int i=1; i<=n; i++) printf("%d ", ans[i]);
		puts(""); return 0;
	}
	puts("YES");
	for(int i=1; i<=n; i++) printf("%d ", ans[i]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值