BZOJ 5249: [2018多省省队联测]IIIDX(贪心 + 线段树)
题意
这一天,\(\mathrm{Konano}\) 接到了一个任务,他需要给正在制作中的游戏 \(\mathrm{《IIIDX》}\) 安排曲目 的解锁顺序。游戏内共有\(n\) 首曲目,每首曲目都会有一个难度 \(d\) ,游戏内第 \(i\) 首曲目会在玩家 Pass 第 \(\lfloor \frac{i}{k} \rfloor\) 首曲目后解锁( \(\lfloor x \rfloor\) 为下取整符号)若 \(\lfloor \frac{i}{k} \rfloor = 0\) ,则说明这首曲目无需解锁
举个例子:当 \(k = 2\) 时,第 \(1\) 首曲目是无需解锁的( \(\lfloor \frac{1}{2} \rfloor = 0\) ),第 \(7\) 首曲目需要玩家Pass 第 \(\lfloor \frac{7}{2} \rfloor= 3\) 首曲目才会被解锁。
\(\mathrm{Konano}\) 的工作,便是安排这些曲目的顺序,使得每次解锁出的曲子的难度不低于作为条件需要玩家通关的曲子的难度,即使得确定顺序后的曲目的难度对于每个 \(i\) 满足
\[d_i \geq d_{\lfloor \frac{i}{k} \rfloor}
\]当然这难不倒曾经在信息学竞赛摸鱼许久的 \(\mathrm{Konano}\) 。那假如是你,你会怎么解决这份任务呢?
\((1 \le n \le 500000, 1 \le k,d \le 10^9)\)
题解
一开始只想到了树上贪心的思路qwq 但是拍了几组就发现错了
原来是 \(d_i\) 互不相同的时候是对的 相同的时候就会被 \(hack\) 掉 这个很容易举出反例
还是简单讲一下贪心思路吧...
我们发现这个题目可以转化成一个树上问题 (因为每个点有且仅有一个父亲(前驱节点)和它有关系)
就是使得原序列字典序尽量大 并且每对父子要满足儿子权值大于等于父亲
不难发现这个我们直接把这棵树建出来 然后按后序遍历倒着分配编号就行了 (可以自行模拟一下qwq)
意思就是我们每次贪心地使得序列在前面的点字典序尽量大 并且 它后面有足够空间来分配的最优方案
这个可以用来做正解的思路 我们考虑 序列从前往后 单独考虑
我们肯定需要当前这个最优 但后面我们不能就草莽就做下决定
所以我们可以考虑 根的为子树预留空位 的贪心
这个我们举 题解 的一个例子来考虑吧
我们将原 \(d\) 数组降序排列 例如
\[9,8,7,6,5,5,5,5,4,3,2
\]
我们令 \(F_i\) 表示第 \(i\) 个结点左边预留了的位置个数 那么 \((i-F_i)\) 就是 \(i\) 左边可用数的数量..
假设结点 \(1\) 的子树大小为 \(7\) 那么我第 \(1\) 个结点的值即为第 \(7\) 大的数 , \(5\)
我们把最右边的 \(5\) (第 \(9\) 位) 给第 \(1\) 个结点
那么我们需要在 \([1,8]\) 区间中预定 \(6\) 个数给第 \(1\) 个结点的子树
接着对于第 \(2\) 个结点 (设其子树大小为 \(s\) )
找到一个 尽量靠左 的位置 \(x\) ( 尽量大 ) 使得 \(x\) 右侧的可用数量都不少于 \(x\)
\[\displaystyle \min_{\forall i \ge x, (i-F_i) \ge s} x
\]而且到第 \(i\) 个点如果有父亲的话 , 父亲的预定额就可以去掉了 ( 只去掉一次 )
但保留父亲占的那一个位置就行了
然后这样就可以保证合法 并且 答案最优了
其实我想讲的是如何用线段树实现这个过程2333
我们其实只要开一颗支持 区间加减 + 区间最小值 的线段树就行了
维护一段区间 \(i-F_i\) 的最小值 我们就可以将那个 \(\forall\) 变成判一个了qwq
如果这段区间这个最小值不可行 并不直接代表整个区间不存在解 但代表如果整个区间全选上是不行的
然后我们取预定额的时候 把后面点需要预定的全都都减去那么多就行了
重点是如何查找那个尽量靠左的位置
你先考虑右区间是否可行 如果右区间 当前 不可行 那么左区间 整个 必不可行 那么直接去右区间看是否有解就行了
如果右区间全部都可行 那么我们去左区间看看是否可行 不进行回溯
如果左区间到底的话发现不可行 那么这个点 在原序列后一个就可行了
有一个小问题 如果这样预留的话 可能查的时候就把那个预留位置给占了啊qwq
但这种情况并不会出现的 因为你当前想要走到左子树 那么右子树必须全部满足
但你之前把右区间一个点直接减到不能走了 所以绝对是不可以出现这种情况的qwq (相当于右区间对左区间 \(chkmin\) )
代码
/**************************************************************
Problem: 5249
User: zjp_shadow
Language: C++
Result: Accepted
Time:5244 ms
Memory:28644 kb
****************************************************************/
#include <bits/stdc++.h>
#define For(i, l, r) for(int i = (l), i##end = (r); i <= i##end; ++i)
#define Fordown(i, r, l) for(int i = (r), i##end = (l); i >= i##end; --i)
#define Set(a, v) memeset(a, v, sizeof(a))
using namespace std;
inline bool chkmin(int &a, int b) { return b < a ? a = b, 1 : 0; }
inline bool chkmax(int &a, int b) { return b > a ? a = b, 1 : 0; }
inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * fh;
}
void File() {
freopen ("iiidx.in", "r", stdin);
freopen ("iiidx.out", "w", stdout);
}
double k;
const int N = 500100;
#define lson o << 1, l, mid
#define rson o << 1 | 1, mid + 1, r
#define Del(o, v) tag[(o)] += (v), minv[(o)] -= (v);
struct Segment_Tree {
int tag[N << 2], minv[N << 2];
void push_down(int o) {
if (!tag[o]) return; Del(o << 1, tag[o]); Del(o << 1 | 1, tag[o]); tag[o] = 0;
}
void push_up(int o) { minv[o] = min(minv[o << 1], minv[o << 1 | 1]); }
void Build(int o, int l, int r) {
tag[o] = 0 ; if (l == r) { minv[o] = l; return ; }
int mid = (l + r) >> 1; Build(lson); Build(rson); push_up(o);
}
void Update(int o, int l, int r, int ul, int ur, int uv) {
if (ul <= l && r <= ur) { Del(o, uv); return; }
int mid = (l + r) >> 1; push_down(o);
if (ul <= mid) Update(lson, ul, ur, uv);
if (ur > mid) Update(rson, ul, ur, uv);
push_up(o);
}
int Query(int o, int l, int r, int qv) {
if (l == r) return (minv[o] >= qv ? l : l + 1);
int mid = (l + r) >> 1, res = 0; push_down(o);
if (minv[o << 1 | 1] >= qv) res = Query(lson, qv);
else res = Query(rson, qv);
push_up(o);
return res;
}
} T;
int Num[N], n, a[N], Size[N], fa[N];
int ans[N], cnt[N];
int main() {
cin >> n >> k;
For (i, 1, n) a[i] = read();
sort(a + 1, a + 1 + n);
reverse(a + 1, a + 1 + n);
Fordown (i, n - 1, 1) if (a[i] == a[i + 1]) cnt[i] = cnt[i + 1] + 1;
Fordown (i, n, 1) {
++ Size[i];
Size[fa[i] = floor(i / k)] += Size[i];
}
T.Build(1, 1, n);
For (i, 1, n) {
if (fa[i] && fa[i] != fa[i - 1]) T.Update(1, 1, n, ans[fa[i]], n, -(Size[fa[i]] - 1));
int res = T.Query(1, 1, n, Size[i]);
res += cnt[res]; ++ cnt[res]; res -= (cnt[res] - 1); ans[i] = res;
T.Update(1, 1, n, res, n, Size[i]);
}
For (i, 1, n) printf ("%d ", a[ans[i]]);
return 0;
}
BZOJ 5249: [2018多省省队联测]IIIDX(贪心 + 线段树)的更多相关文章
- bzoj 5249 [2018多省省队联测] IIIDX
bzoj 5249 [2018多省省队联测] IIIDX Link Solution 首先想到贪心,直接按照从大到小的顺序在后序遍历上一个个填 但是这样会有大问题,就是有相同的数的时候,会使答案不优 ...
- 【刷题】BZOJ 5249 [2018多省省队联测]IIIDX
Description [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在,他在世界知名游戏公司KONMAI内工作,离他的梦想也 ...
- bzoj千题计划324:bzoj5249: [2018多省省队联测]IIIDX(线段树)
https://www.lydsy.com/JudgeOnline/problem.php?id=5249 把树建出来 如果所有的d互不相同,后续遍历即可 现在有的d相同 将d从小到大排序,考虑如何将 ...
- 5249: [2018多省省队联测]IIIDX
5249: [2018多省省队联测]IIIDX 链接 分析: 贪心. 将给定的权值从大到小排序,从第一个往后挨个赋值,考虑第i个位置可以赋值那些树.首先满足前面必须至少有siz[i]个权值没选,如果存 ...
- BZOJ.5249.[九省联考2018]iiidx(贪心 线段树)
BZOJ LOJ 洛谷 \(d_i\)不同就不用说了,建出树来\(DFS\)一遍. 对于\(d_i\)不同的情况: Solution 1: xxy tql! 考虑如何把这些数依次填到树里. 首先对于已 ...
- bzoj 5248: [2018多省省队联测]一双木棋
Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子, 两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子 ...
- bzoj 5251: [2018多省省队联测]劈配
Description 一年一度的综艺节目<中国新代码>又开始了. Zayid从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了. 题目描述 轻车熟路的Zayi ...
- BZOJ 5248: [2018多省省队联测]一双木棋(对抗搜索)
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 439 Solved: 379[Submit][Status][Discuss] Descriptio ...
- 【刷题】BZOJ 5248 [2018多省省队联测]一双木棋
Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子, 两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子 ...
随机推荐
- CF293B Distinct Paths 搜索
传送门 首先数据范围很假 当\(N + M - 1 > K\)的时候就无解 所以对于所有要计算的情况,\(N + M \leq 11\) 超级小是吧,考虑搜索 对于每一个格子试填一个数 对于任意 ...
- CQOI2018简要题解
CQOI2018简要题解 D1T1 破解 D-H 协议 题意 Diffie-Hellman 密钥交换协议是一种简单有效的密钥交换方法.它可以让通讯双方在没有事先约定密钥(密码)的情况下,通过不安全的信 ...
- Qt小项目之串口助手控制LED
Qt小项目之串口助手控制LED 前言 最近刚学了一点Qt开发上位机,尝试着做个小软件练练手.查找了很多资料,做了一个简单的串口助手,可以实现串口基本发送和接收功能,支持中文显示,还可以控制STM32开 ...
- 阿里云centos内docker的搭建
由于docker在17之后的版本分成了docker EE(企业版)和docker CE(社区版),那么我们在安装的时候就要开始纠结的选择了,这里我推荐了docker CE(社区版). 实际上这两个版本 ...
- OpenGL学习(1)——创建窗口
这是我的第一篇博客,试着记录学习OpenGL的过程.使用的教程:LearnOpenGL,系统:Deepin 15.9.3,IDE:Qt Creator. 添加头文件 创建窗口用到两个库:GLFW和GL ...
- js怎么将 base64转换成图片
//获取数组最后一个元素 let hasFiles = files[Object.keys(files).pop()] // 参考上面的图片 let file = hasFiles.url let n ...
- Week2 代码复查
代码复查 http://blog.fogcreek.com/increase-defect-detection-with-our-code-review-checklist-example/ 这篇博客 ...
- A11-java学习-二维数组-面向对象概念-类的编写-测试类的编写-创建对象-使用对象-递归
二维数组的内存结构和使用 引用类型的内存结构 栈区.堆区.方法区.数据栈等内存分析和介绍 面向对象.面向过程区别和发展 类型的定义 引用类型.值类型 预定义类型和自定义类型 类型与对象(实例) 对象的 ...
- 运行scrapy crawl (文件名)时显示invalid syntax和no modle 'win32api'解决方案
使用pycharm爬取知乎网站的时候,在terminal端输入scarpy crawl zhihu,提示语法错误,如下: 原因是python3.7中将async设为关键字,根据错误提示,找到manho ...
- vue js 在组件中对数组使用splice() 遇到的坑。。。
遇到的问题: 用el-dialog写了个子组件 要实现在子组件中增删数据 点击确定后把值返回给父组件 父组件在每次点开子组件时都会把自己的值传进去. //父组件传值 this.$refs.transf ...