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

2023-11-02 10:32

本文主要是介绍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]);
}

这篇关于Codeforces Round 638 (Div. 2)___F. Phoenix and Memory —— 贪心 +线段树的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/330004

相关文章

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

usaco 1.3 Barn Repair(贪心)

思路:用上M块木板时有 M-1 个间隙。目标是让总间隙最大。将相邻两个有牛的牛棚之间间隔的牛棚数排序,选取最大的M-1个作为间隙,其余地方用木板盖住。 做法: 1.若,板(M) 的数目大于或等于 牛棚中有牛的数目(C),则 目测 给每个牛牛发一个板就为最小的需求~ 2.否则,先对 牛牛们的门牌号排序,然后 用一个数组 blank[ ] 记录两门牌号之间的距离,然后 用数组 an

hdu 1754 I Hate It(线段树,单点更新,区间最值)

题意是求一个线段中的最大数。 线段树的模板题,试用了一下交大的模板。效率有点略低。 代码: #include <stdio.h>#include <string.h>#define TREE_SIZE (1 << (20))//const int TREE_SIZE = 200000 + 10;int max(int a, int b){return a > b ? a :

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

poj 3190 优先队列+贪心

题意: 有n头牛,分别给他们挤奶的时间。 然后每头牛挤奶的时候都要在一个stall里面,并且每个stall每次只能占用一头牛。 问最少需要多少个stall,并输出每头牛所在的stall。 e.g 样例: INPUT: 51 102 43 65 84 7 OUTPUT: 412324 HINT: Explanation of the s

poj 2976 分数规划二分贪心(部分对总体的贡献度) poj 3111

poj 2976: 题意: 在n场考试中,每场考试共有b题,答对的题目有a题。 允许去掉k场考试,求能达到的最高正确率是多少。 解析: 假设已知准确率为x,则每场考试对于准确率的贡献值为: a - b * x,将贡献值大的排序排在前面舍弃掉后k个。 然后二分x就行了。 代码: #include <iostream>#include <cstdio>#incl

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

HDU4737线段树

题目大意:给定一系列数,F(i,j)表示对从ai到aj连续求或运算,(i<=j)求F(i,j)<=m的总数。 const int Max_N = 100008 ;int sum[Max_N<<2] , x[Max_N] ;int n , m ;void push_up(int t){sum[t] = sum[t<<1] | sum[t<<1|1] ;}void upd