【题解】$test0628$ 消息传递 - 树形 $dp$ - 换根 $dp$ - 贪心
ybt 1773 消息传递
题目描述
巴蜀国的社会等级森严,除了国王之外,每个人均有且只有一个直接上级,当然国王没有上级。如果 \(A\) 是 \(B\) 的上级,\(B\) 是 \(C\) 的上级,那么 \(A\) 就是 \(C\) 的上级。绝对不会出现这样的关系:\(A\) 是 \(B\) 的上级,\(B\) 也是 \(A\) 的上级。
最开始的时刻是 \(0\),你要做的就是用 \(1\) 单位的时间把一个消息告诉某一个人,让他们自行散布消息。在任意一个时间单位中,任何一个已经接到消息的人,都可以把消息告诉他的一个直接上级或者直接下属。
现在,你想知道:
1.到底需要多长时间,消息才能传遍整个巴蜀国的所有人?
2.要使消息在传递过程中消耗的时间最短,可供选择的人有哪些?
\(\\\)
\(\\\)
\(Solution\)
第一次要写换根 \(dp\) 居然是在考场 = =,一共就 \(5\) 道,三道换根一道期望全都懵逼了 \(QAQ\)
所以啊,只有平时额外下了功夫才可能考试考得好,不能总是考试考了才去学,虽然说吃一堑长一智,但是堑吃多了你就没了啊 = =
所以还是想写篇博客加深一下印象~顺带记录第一道换根(?这有啥好纪念的啊
\(\\\)
基础 \(dp\)
这个还是比较好想的,这个还没想出来的可以出门左拐了
设 \(dp[i]\) 表示从 \(i\) 往子树内传需要的最大时间,转移方程为 $$dp[i] = max(dp[son] + num[son])$$
其中 \(num[son]\) 表示是第几个传给这个儿子的,有一个明显的贪心就是,把 \(dp[son]\) 排个序,从最大的开始赋值 \(1 ~ size[i] - 1\)
单次复杂度 \(O(n\log n)\) ,加上枚举根,总复杂度 \(O(n^2\log n)\)
\(\\\)
换根
设 \(down[i]\) 表示从 \(i\) 往子树内传需要的最大时间,也是就上述的 \(dp\)
设 \(up[i]\) 表示从 \(i\) 往外走,也就是除掉 \(i\) 子树后需要的最大时间
于是就有 $$up[i] = max(up[fa] + num[fa], max { down[k] + num[k] }), k \in son_{fa}, k \neq i$$
这个方程最好画画图,意思就是, 它爸的外部,和它爸的其他儿子,都是 \(i\) 子树的外部,所以一起计算答案
\(\\\)
实现
这道题最难的就是实现啊 \(\kk\) ,口糊方程谁不会啊233
\(1.\) 上面那个求 \(up\) 的方程,如果每一个都扫一遍就是 \(O(n^2)\) 的,所以是在 \(i\) 那里就更新所有的 \(up[son_i]\)
把所有的 \(dp[son_i]\) 和 \(up[i]\) 都丢进数组,排个序,用前缀后缀最大值维护,再枚举儿子,用二分找到它的位置,由于它可以不传递了,所以它后面所有时间都可以减一
for(int i = head[x]; i; i = nxt[i])
if(ver[i] != fa)
g[++ k] = dp[ver[i]] + 1;//所有的儿子的dp
if(down[x]) g[++ k] = down[x];//对8起!我把所有up都写成down 把down都写成dp了 233
sort(g + 1, g + 1 + k, cmp);//按照从大到小排,num直接等于i
beh[0] = beh[k + 1] = pre[0] = pre[k + 1] = 0;//全部清零会T,其实只用把最前面最后面清零即可
F(i, 1, k) beh[i] = max(beh[i - 1], g[i] + i);//由于是从大到小排,所以后缀最大值是前缀
for(int i = k; i >= 1; -- i) pre[i] = max(pre[i + 1], g[i] + i);
F(i, 1, k) g[i] = - g[i]; //二分用lower_bound,是找第一个大于等于的,所以要存负值
for(int i = head[x]; i; i = nxt[i])
if(ver[i] != fa)
{
int pos = lower_bound(g + 1, g + 1 + k, -dp[ver[i]] - 1) - g;
down[ver[i]] = max(pre[pos + 1] - 1, beh[pos - 1]);
}
\(\\\)
\(2.\)要注意的是,取答案时,照理来说应该是 \(max(up[x], down[x])\) ,但是如果两个相等,就必须有个先后,所以还要加一
ans[x] = max(down[x], dp[x]) + (down[x] == dp[x]);
\(\\\)
写完发现其实貌似也不是很难(雾
完结撒花✿✿ヽ(°▽°)ノ✿
\(\\\)
\(\\\)
\(Code\)
#include<bits/stdc++.h>
#define F(i, x, y) for(int i = x; i <= y; ++ i)
using namespace std;
int read();
const int N = 4e5 + 5;
int n, x;
int dp[N], down[N], minn = 0x3f3f3f3f, ans[N], pre[N], beh[N];
int head[N], cnt, ver[N << 1], nxt[N << 1];
void add(int x, int y){ver[++ cnt] = y, nxt[cnt] = head[x], head[x] = cnt;}
bool cmp(int x, int y) {return x > y;}
void dfs(int x, int fa)
{
int g[N], k = 0;
F(i, 0, n) g[i] = 0;
for(int i = head[x]; i; i = nxt[i])
if(ver[i] != fa)
dfs(ver[i], x), g[++ k] = dp[ver[i]];
sort(g + 1, g + 1 + k);
F(i, 1, k) g[i] += k - i + 1, dp[x] = max(dp[x], g[i]);
}
void change_root(int x, int fa)
{
ans[x] = max(down[x], dp[x]) + (down[x] == dp[x]);
int g[N], k = 0;
for(int i = head[x]; i; i = nxt[i])
if(ver[i] != fa)
g[++ k] = dp[ver[i]] + 1;
if(down[x]) g[++ k] = down[x];
sort(g + 1, g + 1 + k, cmp);
beh[0] = beh[k + 1] = pre[0] = pre[k + 1] = 0;
F(i, 1, k) beh[i] = max(beh[i - 1], g[i] + i);
for(int i = k; i >= 1; -- i) pre[i] = max(pre[i + 1], g[i] + i);
F(i, 1, k) g[i] = - g[i];
for(int i = head[x]; i; i = nxt[i])
if(ver[i] != fa)
{
int pos = lower_bound(g + 1, g + 1 + k, -dp[ver[i]] - 1) - g;
down[ver[i]] = max(pre[pos + 1] - 1, beh[pos - 1]);
}
for(int i = head[x]; i; i = nxt[i])
if(ver[i] != fa)
change_root(ver[i], x);
}
int main()
{
n = read();
F(i, 2, n) x = read(), add(i, x), add(x, i);
dfs(1, 0), change_root(1, 0);
F(i, 1, n) minn = min(minn, ans[i]);
printf("%d\n", minn + 1);
F(i, 1, n) if(ans[i] == minn) printf("%d ", i);
return 0;
}
/*--------------- Bn_ff 2020.7.2 ybt1773 ---------------*/
int read()
{
int x = 0, f = 1;
char c = getchar();
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;
}