显然题意可以抽象成:一棵树,要对树上的每个点标上给定的权值,满足每个点上的权值都
≤\leq≤
子树内点的权值,并使这个棵树编号从小到大的权值字典序最大。
首先可以一眼得到一个做法,将权值从大到小排序,把长度为子树大小的一段按子树编号从小到大丢给它们,递归下去得到答案。
这种做法在
did_idi
不重复的时候是正确的,那
did_idi
要是有相同的数呢?
有可能可以将编号
xxx
子树里一个大的权值与编号
x+1x+1x+1
的子树根的权值替换,使得
xxx
的权值依然是能取得的最大数且子树内的数都比
xxx
的权值大,同时
x+1x+1x+1
子树内的点也还能标满
≥\geq≥
x+1x+1x+1
权值的权值。
那怎么做呢?先把给定权值从大到小排序,线段树上维护每个权值左边(包括本身)还能取的权值的个数
CiC_iCi
。
当取好一个点
xxx
的权值时,需要给它子树内的点预留取的权值,这些权值显然在
xxx
取得权值的左边,但是我们并不知道取的是哪些权值,所以只把
xxx
取得的权值右边(包括本身)的
CiC_iCi
减去
xxx
子树大小
size[x]size[x]size[x]
,每次取一个点
yyy
的权值时,只需要在线段树上找到最大权值
valvalval
满足
valvalval
所在位置右边(包括本身)的所有
Ci≥size[y]C_i\geq size[y]Ci≥size[y]
,且
valvalval
尽可能靠右即可,这个在线段树上二分就能做到。
如果一个点有父亲,求它的权值时要把它父亲为子树预留的大小去掉,需要注意的是,每个父亲预留的大小只要去掉一次,写的时候容易忽略这一点,WA了半天。
/*
* @Author: SugarSBN
* @Date: 2018-05-25 17:24:46
* @Language: C++
* @Task:iiidx
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include