[九省联考2018]IIIDX 贪心 线段树
题解:
一开始翻网上题解看了好久都没看懂,感觉很多人都讲得不太详细,所以导致一些细节的地方看不懂,所以这里就写详细一点吧,如果有不对的or不懂的可以发评论在下面。
首先有一个比较明显的50分贪心:
先把d排好序,然后按从小到大的顺序贪心的给每个点选值,同等条件下优先编号大的,于是越小的值会越趋近于放在编号越大的上面。
但是这样在数字重复的情况下是不对的, 比如下面这组数据:
4 3.0
1 1 2 2
贪心会得到1 1 2 2 ,而正确答案是1 2 2 1.
因此换个角度考虑,在什么情况下一个点可以取到一个值x?
设这个点的子树大小为Size[i],那么这个点可以取到值x,当且仅当大于等于x的还没被取的值的个数 >= Size[i],原因应该是很好理解的,毕竟还要有Size[i] - 1和比x大的值放在i的子树上才行。
因此我们先对d从小到大排序去重,统计每个值的出现次数cnt[x], 然后对于每个数统计一个f[x]表示大于等于x的还没被取的值的个数为f[x].
假设我们给节点i匹配上了一个值x,那么这个策决策对小于等于x的值的影响是确定的,因此将所有小于等于x的值的f数组都减小SIze[i],表示这些值的右边可以取的值又减小了Size[i]个。
我们将和这个操作称为“预定”,因为我们现在并不知道点i的子树分别会选择哪些值,但我们知道它们要选几个值,所以我们相当于先告诉后面的人,这个节点i已经坐到了x这个值上,并且要求后面的人为它的子树留Size[i] - 1个座位。
因为这个决策对大于x的值的影响是不确定的,我们暂时没有修改它,但其实这个决策会对它产生影响,那么对于一个值k,在取它之前的决策到底对它产生了什么样的影响呢?
对于一个值k,它的真正的f[k]其实是min(f[1], f[2], f[3] ....f[k]),因为每个f值都相当于一个后缀和,一个合法的值不能使得f[k]反而比它前面的f值更大,因为前面的f值要比f[k]统计了更多的数。
因为题目要求使得字典序最大,所以我们按照编号从小到大给节点分配值显然是最优的。
因此我们每次就是要在剩下的数上寻找一个最靠右的,并且使得min(f[1], f[2], f[3] ...f[k]) >= Size[i]的k。
因为涉及区间修改和查询,我们使用线段树来维护所有的f值,每次选好一个值,我们就把一些已经确定的影响从线段树中删除(对一个区间进行- Size[i]的操作)。
对于每次选值,我们都可以在线段树上进行二分来查找满足上述粗体字要求的最靠右的值。
值得注意的是,在每次查询前,如果一个节点的父亲的影响还没有被撤销的话,应该要撤销它父亲的影响。(即把父亲给子树占的座给加回来 :1 ~ 父亲匹配值的f值 加上 Size[fa] - 1)
为什么呢?
想象一下,如果不撤销父亲的影响的话,岂不是相当于别人特意给你占了座,结果你自己还不能坐上去?
因为一个节点的儿子都是连续的,所以我们在撤销的父亲的影响后,会马上把不应该撤销的部分给补上(儿子的子树在不断的加上来),所以不用担心对之后的决策造成影响。
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define AC 551000
#define ac 2500000 int n, w, go, tot, rnt;
double k;
int ans[AC], cnt[AC], father[AC], Size[AC], f[AC], s[AC];//cnt[i]表示排名第i位的d值出现的次数
bool done[AC]; inline int read()
{
int x = ;char c = getchar();
while(c > '' || c < '') c = getchar();
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x;
} inline int Min(int a, int b){
return a > b ? b : a;
} struct seg_tree{
int tree[ac], lazy[ac], l[ac], r[ac]; inline void update(int x){
tree[x] = Min(tree[x * ], tree[x * + ]);
} inline void pushdown(int x)
{
if(lazy[x])
{
int ll = x * , rr = ll + ;
tree[ll] += lazy[x], tree[rr] += lazy[x];
lazy[ll] += lazy[x], lazy[rr] += lazy[x];
lazy[x] = ;
}
} void build(int x, int ll, int rr)
{
l[x] = ll, r[x] = rr;
if(ll == rr){tree[x] = f[ll]; return ;}
int mid = (ll + rr) >> ;
build(x * , ll, mid), build(x * + , mid + , rr);
update(x);
} void change(int x, int ll, int rr)
{
pushdown(x);
if(l[x] == ll && r[x] == rr)
{
tree[x] += w, lazy[x] += w;
return ;
}
int mid = (l[x] + r[x]) >> ;
if(rr <= mid) change(x * , ll, rr);
else if(ll > mid) change(x * + , ll, rr);
else change(x * , ll, mid), change(x * + , mid + , rr);
update(x);
} void find(int x)
{
pushdown(x);
if(l[x] == r[x]){go = tree[x] >= w ? l[x] : l[x] - ; return ;}
if(tree[x * ] >= w) find(x * + );
else find(x * );
update(x);
}
}T; void pre()
{
n = read(), scanf("%lf", &k);
for(R i = ; i <= n; i ++) s[i] = read(), Size[i] = ;
sort(s + , s + n + );
for(R i = ; i <= n; i ++)
{
++ cnt[tot + ];
if(s[i] != s[i + ]) s[++tot] = s[i];
}
for(R i = tot; i; i --)
f[i] = f[i + ] + cnt[i];//存下每个值右边有多少个可供选择的值
for(R i = n; i; i --)
father[i] = floor(i / k), Size[father[i]] += Size[i];//获取每个节点的Size
T.build(, , tot);
} void work()
{
for(R i = ; i <= n; i ++)
{
int fa = floor(i / k);
if(fa && !done[fa]) w = Size[fa] - , T.change(, , ans[fa]);//如果有父亲的话,要先把给儿子预定的节点还回来以帮助正确判断
w = Size[i], done[fa] = true;
T.find(), w = -Size[i];//先找到一个合法的点
T.change(, , go);//再减去已经被预定的位置
ans[i] = go;
printf("%d ", s[go]);
}
printf("\n");
} int main()
{
// freopen("in.in", "r", stdin);
pre();
work();
// fclose(stdin);
return ;
}
[九省联考2018]IIIDX 贪心 线段树的更多相关文章
- BZOJ.5249.[九省联考2018]iiidx(贪心 线段树)
BZOJ LOJ 洛谷 \(d_i\)不同就不用说了,建出树来\(DFS\)一遍. 对于\(d_i\)不同的情况: Solution 1: xxy tql! 考虑如何把这些数依次填到树里. 首先对于已 ...
- 洛谷P4364 [九省联考2018]IIIDX 【线段树】
题目 [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在 ,他在世界知名游戏公司KONMAI内工作,离他的梦想也越来越近了.这款 ...
- [BZOJ5249][九省联考2018]IIIDX:线段树+贪心
分析 GXZlegend orz 构造出一组合法的解并不是难事,但是我们需要输出的是字典序最大的解. 字典序最大有另一种理解方式,就是让越小的数尽量越靠后. 我们从树的根结点出发,从1开始填数,构造出 ...
- 洛谷P4364 [九省联考2018]IIIDX(线段树)
传送门 题解看得……很……迷? 因为取完一个数后,它的子树中只能取权值小于等于它的数.我们先把权值从大到小排序,然后记$a_i$为他左边(包括自己)所有取完他还能取的数的个数.那么当取完一个点$x$的 ...
- [luogu] P4364 [九省联考2018]IIIDX(贪心)
P4364 [九省联考2018]IIIDX 题目背景 Osu 听过没?那是Konano 最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在,他在世界知名游戏公司KONMAI ...
- [九省联考2018] IIIDX 线段树+贪心
题目: 给出 k 和 n 个数,构造一个序列使得 d[i]>=d[i/k] ,并且字典序最大. 分析: 听说,当年省选的时候,这道题挡住了大批的高手,看上去十分简单,实际上那道弯段时间内是转不过 ...
- [九省联考2018]IIIDX
题目描述 这一天,Konano接到了一个任务,他需要给正在制作中的游戏<IIIDX>安排曲目的解锁顺序.游戏内共有n首曲目 ,每首曲目都会有一个难度d,游戏内第i首曲目会在玩家Pass第t ...
- BZOJ5249:[九省联考2018]IIIDX——题解
https://www.luogu.org/problemnew/show/P4364#sub https://www.lydsy.com/JudgeOnline/problem.php?id=524 ...
- [luogu]P4364 [九省联考2018]IIIDX
题目背景 Osu 听过没?那是Konano 最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在,他在世界知名游戏公司KONMAI 内工作,离他的梦想也越来越近了. 这款音乐 ...
随机推荐
- Ubuntu 安装 搜狗输入法
1.去下载搜狗输入法安装包: https://pinyin.sogou.com/linux/ 2.安装 sudo dpkg sougou****.deb 3.去设置 参考:https://blog.c ...
- 两分钟了解Docker的优势
本文来自网易云社区 我们主要从Docker对业务架构和生产实践的角度来分析. 随着业务规模的逐渐扩大,产品复杂度也随着增加,企业需要解决快速迭代.高可靠和高可用等问题,一个自然的选择是服务化的拆分,把 ...
- 「日常训练」Known Notation(ZOJ-3829)
题意与分析 题意是这样的:给一个字符串,字符串中只包含数字和运算符'*'.现在问字符串是不是一个合法的逆波兰式(后缀表达式).已知逆波兰式的空格消除,也就是说123可以看成123也可以看成1和23.如 ...
- 「日常训练」Caterpillar(POJ-3310)
题意与分析 一条很有趣的题目.给一个无向图,问它是否无环,且可以在上面找到一条线,使所有的顶点要么在线上要么不在线上但在与线相连的边上. 那么首先要确定所有点联系在一起.这个可以同判环一起处理:如果建 ...
- Python3.6+selenium3.8+Firefox5.7 环境搭建
大家好,我是阿哲,本人是从php转岗过来学习测试的一名小菜! 在学习selenium过程中,发现运行selenium有很多的问题. 我们在利用pip install selenium 安装的最新版后, ...
- 为什么Python在列表和元组的末尾允许使用逗号?
Python 允许您在列表,元组和字典的末尾添加一个尾随逗号: [1, 2, 3,] ('a', 'b', 'c',) d = { "A": [1, 5], "B&quo ...
- 关于axios跨域带cookie
axios 设置 withCredentials :true $u = $_SERVER['HTTP_REFERER'];$u = preg_replace('#/$#', '', $u);head ...
- Java学习 · 初识 容器和数据结构
容器和数据结构 1. 集合的引入 a) 集合的使用场景:需要将一些相同结构的个体整合到一起时 i. 新闻列表 ii. 邮件列表 iii. ...
- 聊聊、dubbo 找不到 dubbo.xsd 报错
平常在用 Dubbo 的时候,创建 xml 会提示 http://code.alibabatech.com/schema/dubbo/dubbo.xsd 找不到. 大家可以去 https://gith ...
- Python3 Tkinter-Frame
1.创建 from tkinter import * root=Tk() for fm in ['red','blue','yellow','green','white','black']: Fram ...