• 【题解】$test0628$ 消息传递


    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(nlog n)) ,加上枚举根,总复杂度 (O(n^2log 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 eq 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
    ", 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;
    }
    
  • 相关阅读:
    HTML转换成word文档
    泛型的导入导出的公用方法
    封装的分页jq
    用泛型做的分页类
    EntityFramework小知识
    泛型连接数据库
    在博客园看的一些小技巧
    渗透之路基础 -- SQL注入
    第一篇博客就这么开始了?!稍微介绍一下。
    正则基础用法
  • 原文地址:https://www.cnblogs.com/Bn_ff/p/13210517.html
Copyright © 2020-2023  润新知