把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【题解】$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;
}
posted @ 2020-07-02 15:18  Bn_ff  阅读(215)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end