~~~题面~~~

题解:

  一开始翻网上题解看了好久都没看懂,感觉很多人都讲得不太详细,所以导致一些细节的地方看不懂,所以这里就写详细一点吧,如果有不对的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 贪心 线段树的更多相关文章

  1. BZOJ.5249.[九省联考2018]iiidx(贪心 线段树)

    BZOJ LOJ 洛谷 \(d_i\)不同就不用说了,建出树来\(DFS\)一遍. 对于\(d_i\)不同的情况: Solution 1: xxy tql! 考虑如何把这些数依次填到树里. 首先对于已 ...

  2. 洛谷P4364 [九省联考2018]IIIDX 【线段树】

    题目 [题目背景] Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在 ,他在世界知名游戏公司KONMAI内工作,离他的梦想也越来越近了.这款 ...

  3. [BZOJ5249][九省联考2018]IIIDX:线段树+贪心

    分析 GXZlegend orz 构造出一组合法的解并不是难事,但是我们需要输出的是字典序最大的解. 字典序最大有另一种理解方式,就是让越小的数尽量越靠后. 我们从树的根结点出发,从1开始填数,构造出 ...

  4. 洛谷P4364 [九省联考2018]IIIDX(线段树)

    传送门 题解看得……很……迷? 因为取完一个数后,它的子树中只能取权值小于等于它的数.我们先把权值从大到小排序,然后记$a_i$为他左边(包括自己)所有取完他还能取的数的个数.那么当取完一个点$x$的 ...

  5. [luogu] P4364 [九省联考2018]IIIDX(贪心)

    P4364 [九省联考2018]IIIDX 题目背景 Osu 听过没?那是Konano 最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在,他在世界知名游戏公司KONMAI ...

  6. [九省联考2018] IIIDX 线段树+贪心

    题目: 给出 k 和 n 个数,构造一个序列使得 d[i]>=d[i/k] ,并且字典序最大. 分析: 听说,当年省选的时候,这道题挡住了大批的高手,看上去十分简单,实际上那道弯段时间内是转不过 ...

  7. [九省联考2018]IIIDX

    题目描述 这一天,Konano接到了一个任务,他需要给正在制作中的游戏<IIIDX>安排曲目的解锁顺序.游戏内共有n首曲目 ,每首曲目都会有一个难度d,游戏内第i首曲目会在玩家Pass第t ...

  8. BZOJ5249:[九省联考2018]IIIDX——题解

    https://www.luogu.org/problemnew/show/P4364#sub https://www.lydsy.com/JudgeOnline/problem.php?id=524 ...

  9. [luogu]P4364 [九省联考2018]IIIDX

    题目背景 Osu 听过没?那是Konano 最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在,他在世界知名游戏公司KONMAI 内工作,离他的梦想也越来越近了. 这款音乐 ...

随机推荐

  1. Java基础知识总结一

    1.何为编程? 编程就是让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程. 为了使计算机能够理解人的意图,人类就必须要将需解决的问题的思路.方法.和手段通过计算机能够理解 ...

  2. mysql数据库基本操作命令

    1.登录命令 mysql -u root -p "password" 2.列出所有数据库 show databases; 3.使用数据库 use db_name 4.列出数据库中所 ...

  3. Jmeter登录接口返回 status415

    1.现象:在查看结果树中看到: Request Headers:Connection: keep-aliveContent-Type: application/x-www-form-urlencode ...

  4. Linux命令应用大词典-第28章 硬件管理

    28.1 lscpu:显示有关CPU架构的信息 28.2 nproc:显示当前进程可用的CPU数目 28.3 chcpu:配置CPU

  5. Java基础:关键字final,static

    一 . final 含义:adj.最后的,最终的; 决定性的; 不可更改的.在Java中是一个保留的关键字,可以声明成员变量.方法.类以及本地变量.一旦你将引用声明作final,你将不能改变这个引用了 ...

  6. Memcache的客户端连接系列(四) PHP

    关键词: Memcached   PHP 客户端 声明:本文并非原创,转自华为云帮助中心的分布式缓存服务(Memcached)的用户指南.客户端连接方法通用,故摘抄过来分享给大家. PHP客户端 Re ...

  7. 01背包问题:DP

    题目描述: 有 N 件物品和一个容量是 V 的背包.每件物品只能使用一次. 第 i 件物品的体积是 vi,价值是 wi. 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大.输出 ...

  8. 【模板】DFS

    int dx[] = { 0,1,0,-1 }; int dy[] = { 1,0,-1,0 }; void dfs()//参数用来表示状态 { if (到达终点状态) { ...//根据题意来添加 ...

  9. Leetcode - 557. Reverse Words in a String III (C++) stringstream

    1. 题目:https://leetcode.com/problems/reverse-words-in-a-string-iii/discuss/ 反转字符串中的所有单词. 2. 思路: 这题主要是 ...

  10. UVa 455 - Periodic Strings - ( C++ ) - 解题报告

    1.题目大意 求一个长度不超过80的字符串的最小周期. 2.思路 非常简单,基本就是根据周期的定义做出来的,几乎不需要过脑. 3.应该注意的地方 (1) 最后输出的方式要注意,不然很容易就PE了.不过 ...