UOJ NOI Round #5 d1t3
tag:思博,树形dp
先解决第一个问题,给定一个 ddm 序判断合法。
先考虑 ddm 序最小的那个叶子 \(x\),假设深度为 \(dep\),那么 \(x\) 到根链上这些点的 ddm 序从上到下一定是 \([1,dep]\)。
于是不难发现,若 \(a_x\notin[1,dep]\),那么一定不合法。
由于这条链包含了前 \(dep\) 小,那么如果 \(a_x\) 不在链上,即便它可以经过一些操作换到链上,它也无法继续往下走了。
否则直接把 \(a_x\) 依次换下来即可,这样会最小化对树形态的影响。
这样操作完以后,实际上是可以把这个叶子扔掉的,而且新的树的合法性和原树相同。
可以这样考虑,对于链外的点,因为交换操作只在链上进行,所以不管怎么交换,链上始终是前 \(dep\) 小,不会影响到链外的点换到链上。
而对于链上的点,这样依次操作之后,不会改变它们的相对顺序,所以也不会影响到它们之间的交换。
这里实际上是按照后序遍历进行构造,后序遍历指按照 ddm 序从小到大依次处理每个儿子,然后再处理自己
但是这样还是无法得到一个靠谱的做法,考虑总结一个结论:
设 \(mx[x]\) 为子树 ddm 序最大值,那么有解当且仅当 \(\forall x,mx[x]\ge a[x]\)
感性证明,可以借助上述过程,只要我们保证在上述递归过程中,每一次选择的叶子都合法即可。换句话说,要保证每次最左边的叶子 \(x\),\(a_x\) 在 \(x\) 到根链上出现过。
对于第 \(k\) 步,假设选择的叶子为 \(x\),那么 \(x\) 到根链的点权构成的集合一定为 \([1,mx[x]]\) 除去之前用过的 \(a_i\)。
可以这样理解,由于是按照后序遍历构造,那么处理到当前点时,前 \(k\) 步到根链的权值集合的并,一定刚好等于 \(x\) 到根以及 \(x\) “左边” 所有兄弟子树的点权的并,也就是\([1,mx[k]]\)。
所以实际上只需要满足 \(mx[x]\ge a[x]\) 就可以保证 \(a[x]\) 在到根链上出现过了(因为 \(a_i\) 互不相同)。
然后就可以拿到 \(2,5\) 的部分分了。
考虑一个 dp,设 \(f[x]\) 为 \(x\) 的权值至少需要 \(f[x]\) 才能使子树全部合法。
那么转移很简单,初始化为 \(f[x]=a[x]-sz[x]+1\),然后可以贪心,把儿子按照 \(f\) 值从小到大处理,对于第 \(i\) 个儿子,它对 \(x\) 的限制就是至少要 \(f[i]-\)前\(i-1\)个儿子的\(sz\)和。
只有 \(f[1]=1\) 时合法,然后按照刚才的顺序 dfs 就行了。
#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void Read(T &n){
char ch; bool flag=false;
while(!isdigit(ch=getchar()))if(ch=='-')flag=true;
for(n=ch^48;isdigit(ch=getchar());n=(n<<1)+(n<<3)+(ch^48));
if(flag)n=-n;
}
enum{
MAXN = 100005
};
int n, a[MAXN], mn[MAXN], dfn[MAXN], sz[MAXN], cnt;
vector<int>to[MAXN];
inline bool cmp(const int &u, const int &v){return mn[u]<mn[v];}
void dfs(int x){
for(int v:to[x]) dfs(v);
sort(to[x].begin(),to[x].end(),cmp); sz[x] = 1;
for(int v:to[x]) mn[x] = max(mn[x],mn[v]-sz[x]), sz[x] += sz[v];
mn[x] = max(mn[x],a[x]-sz[x]+1);
}
void mk(int x){dfn[x]=++cnt;for(int v:to[x])mk(v);}
int main(){
int Case; Read(Case);
Read(n);
for(int i=1; i<=n; i++) Read(a[i]);
for(int i=2, p; i<=n; i++) Read(p), to[p].push_back(i);
dfs(1);
if(mn[1]!=1) return puts("-1"), 0;
mk(1);
for(int i=1; i<=n; i++) printf("%d ",dfn[i]);puts("");
return 0;
}
/*
1
8
8 4 6 1 5 2 7 3
1 1 1 1 1 1 1
*/