BZOJ5249: [2018多省省队联测]IIIDX(线段树 贪心)
题意
Sol
不难发现题目给出的是一个树,其中\(\frac{i}{K}\)是\(i\)的父亲节点
首先,当\(d_i\)互不相同时,一个显然的贪心策略就是优先给编号小的分配较大的权值。可以排序后dfs完成。
但是,当\(d_i\)相同时,可能存在这样一种情况:把编号小的子树内权值较大的节点,和某个编号较大的根交换后,仍然满足要求
比如\(N = 4, K = 2, a = {1, 1, 1, 2}\)
此时直接贪心的话会输出\(1, 1, 1, 2\),实际上最优解为\(1, 1, 2, 1\)
这时候怎么办呢?
考虑一个节点\(p\)可以选择权值为\(x\)的条件是什么,因为该节点子树内的权值一定都比\(x\)大
因此对于每个权值小于\(x\)的数,权值比它大且可以选择的数至少为\(siz[p]\)个
同时根据贪心的策略,先遍历到的节点应该选大的权值。
这样就不难得到一个算法:
首先按权值从大到小排序,同时用线段树维护出每个位置权值比它大且能选择的位置个数
对于每个点\(p\),在线段树上二分出最大的满足条件的位置\(x\)。同时,当权值相同时,该位置应该更靠右。
然后再在区间\([x, n]\)中的所有点减去\(siz[x]\)
注意一个细节,当遍历到某个节点时,应该消去父亲对他的影响
写完代码 -> 过样例 -> 1A hhhhhh(虽然是抄的)
60分
#include<bits/stdc++.h>
#define sz(x) (int)x.size()
using namespace std;
const int MAXN = 5e5 + 10;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int N; double K;
int a[MAXN], ans[MAXN], res;
vector<int> v[MAXN];
int AddEdge(int x, int y) {
v[x].push_back(y);
}
void dfs(int x) {
for(int i = 0; i < sz(v[x]); i++) dfs(v[x][i]);
if(x) ans[x] = a[res--];
}
int main() {
scanf("%d%lf", &N, &K); res = N;
//printf("%lf\n", K);
for(int i = 1; i <= N; i++) {
int pre = (int) floor(i / K);
a[i] = read(); AddEdge(pre, i);
}
sort(a + 1, a + N + 1);
dfs(0);
for(int i = 1; i <= N; i++) printf("%d ", ans[i]);
return 0;
}
AC代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e5 + 10;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
int N; double K;
int cnt[MAXN], fa[MAXN], siz[MAXN], ans[MAXN], a[MAXN];
#define ls k << 1
#define rs k << 1 | 1
struct Node {
int l, r, f, mn;
}T[MAXN << 2];
void update(int k) {
T[k].mn = min(T[ls].mn, T[rs].mn);
}
void add(int k, int val) {
T[k].f += val; T[k].mn += val;
}
void pushdown(int k) {
if(!T[k].f) return ;
add(ls, T[k].f); add(rs, T[k].f);
T[k].f = 0;
}
void Build(int k, int ll, int rr) {
T[k] = (Node) {ll, rr, 0, 0};
if(ll == rr) {T[k].mn = ll; return ;}
int mid = ll + rr >> 1;
Build(ls, ll, mid); Build(rs, mid + 1, rr);
update(k);
}
void IntervalAdd(int k, int ll, int rr, int val) {
if(ll <= T[k].l && T[k].r <= rr) {add(k, val); return ;}
pushdown(k);
int mid = T[k].l + T[k].r >> 1;
if(ll <= mid) IntervalAdd(ls, ll, rr, val);
if(rr > mid) IntervalAdd(rs, ll, rr, val);
update(k);
}
int Query(int k, int sz) {
if(T[k].l == T[k].r) return T[k].mn >= sz ? T[k].l : T[k].l + 1;
pushdown(k);
if(T[rs].mn >= sz) return Query(ls, sz);
else return Query(rs, sz);
}
int main() {
N = read(); cin >> K;
for(int i = 1; i <= N; i++) {
a[i] = read();
int t = (int) floor(i / K); fa[i] = t; siz[i] = 1;
}
for(int i = N; i >= 0; i--) siz[fa[i]] += siz[i];
Build(1, 1, N);
sort(a + 1, a + N + 1, greater<int>());
for(int i = N - 1; i >= 1; i--) cnt[i] = (a[i] == a[i + 1] ? cnt[i + 1] + 1 : 0);
for(int i = 1; i <= N; i++) {
if(fa[i] && fa[i] != fa[i - 1]) IntervalAdd(1, ans[fa[i]], N, siz[fa[i]] - 1);
int t = Query(1, siz[i]); t += cnt[t]; cnt[t]++; t -= (cnt[t] - 1);
ans[i] = t;
IntervalAdd(1, t, N, -siz[i]);
}
for(int i = 1; i <= N; i++) printf("%d ", a[ans[i]]);
return 0;
}
BZOJ5249: [2018多省省队联测]IIIDX(线段树 贪心)的更多相关文章
- bzoj5249 [2018多省省队联测]IIIDX
转化一下问题变成给定一棵树,一个序列,求父亲的权值小于子树的最大方案. 直接贪心会在有重复权值时出现错误,我们考虑用线段树优化贪心. 将序列从小到大排序,线段树上每个点记录他和他右边当前还可用的权值, ...
- 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 5249 [2018多省省队联测] IIIDX Link Solution 首先想到贪心,直接按照从大到小的顺序在后序遍历上一个个填 但是这样会有大问题,就是有相同的数的时候,会使答案不优 ...
- BZOJ 5249: [2018多省省队联测]IIIDX(贪心 + 线段树)
题意 这一天,\(\mathrm{Konano}\) 接到了一个任务,他需要给正在制作中的游戏 \(\mathrm{<IIIDX>}\) 安排曲目 的解锁顺序.游戏内共有\(n\) 首曲目 ...
- 【刷题】BZOJ 5249 [2018多省省队联测]IIIDX
Description [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在,他在世界知名游戏公司KONMAI内工作,离他的梦想也 ...
- bzoj5252 [2018多省省队联测]林克卡特树
斜率优化树形dp?? 我们先将问题转化成在树上选K+1条互不相交路径,使其权值和最大. 然后我们考虑60分的dp,直接维护每个点子树内选了几条路径,然后该点和0/1/2条路径相连 然后我们会发现最后的 ...
- bzoj 5252: [2018多省省队联测]林克卡特树
Description 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战. 游戏中有一个叫做& ...
- [九省联考2018] IIIDX 线段树+贪心
题目: 给出 k 和 n 个数,构造一个序列使得 d[i]>=d[i/k] ,并且字典序最大. 分析: 听说,当年省选的时候,这道题挡住了大批的高手,看上去十分简单,实际上那道弯段时间内是转不过 ...
随机推荐
- Javascript 上传文件到Azure存储
对一些前端工程师来讲,使用javascript上传文件到Azure存储中可能是需要掌握的技能,希望这篇博客能给到帮助. 在开始前我们需要了解以下几点: 共享访问签名(Shared Access Sig ...
- Java ExecutorService 四种线程池
1.new Thread的弊端 new Thead(new Runnable(){ @Override public void run() { // TODO Auto-generated metho ...
- EA添加时序图
在项目浏览器的空白处右击 http://blog.csdn.net/craftsman1970/article/details/70877530 不同于大部分面向对象或者UML的书籍,在讨论完类图/对 ...
- LeetCode4. 两个排序数组的中位数
4. 两个排序数组的中位数 问题描述 There are two sorted arrays nums1 and nums2 of size m and n respectively.Find the ...
- 洛谷 P3380 【模板】二逼平衡树(树套树)
题面 luogu 题解 2019年AC的第一道题~~ 函数名命名为rank竟然会ce 我写的是树状数组套值域线段树(动态开点) 操作1:询问\(k\)在\([l-r]\)这段区间有多少数比它小,再加\ ...
- vue脚手架初始化的项目 npm run build 无效,没有反应
找到build文件夹的check-versions文件,注释掉如图所以代码即可.
- SPOJ - DQUERY 莫队
题意:给定\(a[1...n]\),\(Q\)次询问,每次统计\([L,R]\)范围内有多少个不同的数字 xjb乱写就A了,莫队真好玩 #include<iostream> #includ ...
- POJ - 2248 迭代加深
枚举ak的值 反向枚举使ak尽快到达最短链的n /*H E A D*/ int n,m,a[23333],dep; bool dfs(int x){ if(a[x-1]>n||a[x-1]< ...
- 【mysql】count(*),count(1)与count(column)区别
https://blog.csdn.net/lzm18064126848/article/details/50491956 count(*)对行的数目进行计算,包含NULL count(column) ...
- Web Worker 案例
什么是 Web Worker? 当在 HTML 页面中执行脚本时,页面的状态是不可响应的,直到脚本已完成. web worker 是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性 ...