bzoj3743 [Coci2015]Kamp 常州模拟赛d6t2

3743: [Coci2015]Kamp

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 484  Solved: 229
[Submit][Status][Discuss]

Description

一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的。

有K个人(分布在K个不同的点)要集中到一个点举行聚会。

聚会结束后需要一辆车从举行聚会的这点出发,把这K个人分别送回去。

请你回答,对于i=1~n,如果在第i个点举行聚会,司机最少需要多少时间把K个人都送回家。

Input

第一行两个数,n,K。

接下来n-1行,每行三个数,x,y,z表示x到y之间有一条需要花费z时间的边。

接下来K行,每行一个数,表示K个人的分布。

Output

输出n个数,第i行的数表示:如果在第i个点举行聚会,司机需要的最少时间。

Sample Input

7 2

1 2 4

1 3 1

2 5 1

2 4 2

4 7 3

4 6 2

3

7

Sample Output

11

15

10

13

16

15

10

HINT

【数据规模】

K <= N <= 500000

1 <= x,y <= N, 1 <= z <= 1000000

分析:本来想用状压dp的,但是n这么大,怎么压的下来。其实这道题有一个结论:如果我们把要送达的点建立一棵树,那么答案就是这棵树上的边权和*2+i到这棵树的最短距离(假设为j点)-j到树上最远一点的距离.其实也很好想为什么,要遍历树上的点最少肯定要每条边都走两次,i点要到这棵树上肯定要走最短距离,我们到一个点后就不回起点了,那么肯定终点在最远的那个点上.

那么想要做出这道题就要用到4个dfs.首先求出每个点在不在树上,然后求出i到树上最近的点和最短距离,接下来求出每个点到它的叶子节点的最长距离和次长距离,最后求出每个点的最长距离.

第一步和第二步很好理解,关键是最后两步,我们求最长距离为什么还要求每个点到它的叶子节点的最长距离和次长距离呢?其实这有点像树链剖分,我们把树链分为最长链和次长链,位于最长链的儿子成为重儿子,重儿子的最远距离点肯定在它的子节点中或者次长链中,这就包含了最长链和次长链两种情况,没有其它情况了,轻儿子的最远距离也肯定在它的子节点或者最长链中,所以我们先求出父节点到子节点的最远距离和次远距离,然后利用父节点更新子节点,很难想到啊.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 500010;

int n, k,head[maxn],to[maxn * 2],nextt[maxn * 2],w[maxn * 2],tot = 1,ner[maxn],son[maxn];
long long mind[maxn],maxd[maxn],secd[maxn],d[maxn],sum;
bool flag[maxn],vis[maxn];

void add(int x, int y, int z)
{
    w[tot] = z;
    to[tot] = y;
    nextt[tot] = head[x];
    head[x] = tot++;
}

bool dfs1(int x)   //找树
{
    vis[x] = 1;
    for (int i = head[x]; i; i = nextt[i])
    {
        int v = to[i];
        if (!vis[v])
        {
            if (dfs1(v))
            {
                flag[x] = 1;
                sum += w[i];
            }
        }
    }
    return flag[x];
}

void dfs2(int x)  //找最短距离
{
    vis[x] = 1;
    if (flag[x])
        ner[x] = x;
    for (int i = head[x]; i; i = nextt[i])
    {
        int v = to[i];
        if (!vis[v] && !flag[v])
        {
            mind[v] = mind[x] + w[i];
            ner[v] = ner[x];
        }
        if (!vis[v])
            dfs2(v);
    }
}

long long dfs3(int x)  //最长距离+次长距离
{
    vis[x] = 1;
    for (int i = head[x]; i; i = nextt[i])
    {
        int v = to[i];
        if (!vis[v] && flag[v])
        {
            long long t = dfs3(v) + w[i];
            if (t > maxd[x])
            {
                secd[x] = maxd[x];
                maxd[x] = t;
                son[x] = v;
            }
            else
                if (t > secd[x])
                    secd[x] = t;
        }
    }
    return maxd[x];
}

void dfs4(int x, long long lmax)   //最后的更新
{
    vis[x] = 1;
    for (int i = head[x]; i; i = nextt[i])
    {
        int v = to[i];
        d[x] = max(lmax, maxd[x]);
        if (flag[v] && !vis[v])
        {
            if (v == son[x])
                dfs4(v, max(lmax, secd[x]) + w[i]);
            else
                dfs4(v, max(lmax, maxd[x]) + w[i]);
        }
    }
}

int main()
{
    memset(flag, false, sizeof(flag));
    scanf("%d%d", &n, &k);
    for (int i = 1; i < n; i++)
    {
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        add(x, y, z);
        add(y, x, z);
    }
    int t;
    for (int i = 1; i <= k; i++)
    {
        scanf("%d", &t);
        flag[t] = 1;
    }
    dfs1(t);
    memset(vis, 0, sizeof(vis));
    dfs2(t);
    memset(vis, 0, sizeof(vis));
    dfs3(t);
    memset(vis, 0, sizeof(vis));
    dfs4(t, 0);
    for (int i = 1; i <= n; i++)
        printf("%lld\n", sum * 2 + mind[i] - d[ner[i]]);

    return 0;
}
时间: 2024-07-30 10:17:32

bzoj3743 [Coci2015]Kamp 常州模拟赛d6t2的相关文章

常州模拟赛d3t2 灰狼呼唤着同胞

题目背景 我的母亲柯蒂丽亚,是一个舞者.身披罗纱,一身异国装扮的她,来自灰狼的村子. 曾经在灰狼村子担任女侍的她,被认定在某晚犯下可怕的罪行之后,被赶出了村子. 一切的元凶,都要回到母亲犯下重罪的那一晚. 题目描述 我不认为柯蒂丽亚有犯罪. 二十年前的混沌,一共有n块碎片. 这n块碎片曾经两两之间都有联系,可是很多联系都在时间的洪流中消失了. 现在,我只能确定其中m条联系的种类. 每条联系都是一条无向边,任意两块碎片之间至多有一条联系,没有联系会连接在同一块碎片的两端. 联系有两种.一种是冲突,

常州模拟赛d5t2 mogician

分析:一个暴力的思想是枚举g,然后枚举每个数ai,看能不能符合要求,这样复杂度是O(nA)的,直接T掉了.也没什么其他的办法了,在暴力的基础上优化一下,优化的关键是要如何快速统计出不满足要求的数的个数.利用数据结构?想不到.仔细分析一下,不满足要求的数组成了很多区间,每次i枚举g的倍数,不满足要求的数的区间总在[i + k + 1,i + g - 1]中,因为i+k+1通过减k满足不了要求,i+g-1比g的倍数少了1,那么利用前缀和数组维护一下个数就好了.不过有一种特殊情况:k >= g - 1

常州模拟赛d5t1 journalist

分析:出题人丧心病狂卡spfa......只能用dijkstar+堆优化. 主要的难点是字典序的处理上,一个想法是在做最短路的时候处理,边松弛边记录,比个大小记录最佳答案.具体的思路大概和最短路计数差不多,当遇到d[u] + w[i] == d[v]是,说明到d[v]有两条最短路了,更新一下答案. 但是这样效率太低了,每一次松弛都要更新一次,能不能一次性更新完呢?可以的.我们在跑完最短路后把满足d[u] + w[i] == d[v]的点v加入u的邻接表中,排个序,然后dfs一遍就好了. #inc

常州模拟赛d2t1 小X的质数

题目背景 小 X 是一位热爱数学的男孩子,在茫茫的数字中,他对质数更有一种独特的 情感.小 X 认为,质数是一切自然数起源的地方. 题目描述 在小 X 的认知里,质数是除了本身和 1 以外,没有其他因数的数字. 但由于小 X 对质数的热爱超乎寻常,所以小 X 同样喜欢那些虽然不是质数, 但却是由两个质数相乘得来的数. 于是,我们定义,一个数是小 X 喜欢的数,当且仅当其是一个质数,或是两 个质数的乘积. 而现在,小 X 想要知道,在 L 到 R 之间,有多少数是他喜欢的数呢? 输入输出格式 输入

常州模拟赛d7t1 亲戚

分析:把题目换个方式理解,就是把各个点排成一列,并且指定了若干对的先后次序,问你有多少种序列满足要求. 显然是一道dp题,直接推出方程似乎有点点困难,那么先看看数据特点. 1.有一些点满足fi=0,那么直接输出n!就可以了. 2.保证所有的关系是一条链.我们假设所有链的长度和为sum,链1的长度为l1,链2的长度为l2...... 现在有sum个位置,我要把链1上的点按照次序放到sum个位置上,一共有C(sum,l1)种方案,接下来放链2,还有sum - l1个位置,所以有C(sum - l1,

常州模拟赛d5t3 appoint

分析:这道题比较奇葩.因为字符串没有swap函数,所以一个一个字符串交换只有30分.但是我们可以不用直接交换字符串,而是交换字符串的指针,相当于当前位置是哪一个字符串,每次交换int,可以拿60分. 对于二维问题,通常转化为一维问题去考虑,得到适当的方法再应用到二维上来,这道题如果转移到一维上就是给你一个序列,每次交换一对区间,区间不重叠,最后要求顺序输出整个序列,很显然,我们只要记录每个数旁边的数就好了,所以用链表能很快解决.转化到二维上,我们记录一个右方的链表,下方的链表,每次交换操作只需要

常州模拟赛d7t3 水管

分析:第一问还是很好做的,关键是怎么做第二问.我们可以每次删掉最小生成树上的一条边,然后再求一次最小生成树,看边权和大小和原来的是不是一样的,不过这个做法效率很低. 考虑Kruskal算法的原理,每次加边权最小的边,如果边上的两个点不连通.如果在最小生成树的基础上把不是上面的边给加上去,就会形成环,在环上找除了这条边之外的最大边权,如果等于新加入的这条边,那么就有多个最小生成树.为什么这样呢?我们把最大边拿掉,添加进这条边,两个点还是连通的,边权和一定,只是在Kruskal的时候先考虑了那条最大

常州模拟赛d8t2 绘画

分析:考虑记录每个坐标上每个颜色出现了几次,并由此算出每个颜色在这个坐标上的贡献.答案肯定是原图的答案扣去矩形的答案,再加上那个矩形同种颜色的贡献,这里的答案指的是Σdis.我们先要记录每个颜色在各个位置出现的次数,因为每一次都是区间操作嘛,所以我们用二维差分可以很好地维护,前缀和求出出现的次数. 然后求出每个位置原本图的和副本的差距,求一下前缀和就得到原本图整体的答案. 最后再用一个前缀和数组求出每个位置覆盖为颜色x后的贡献,就可以了. #include <cstdio> #include

【BZOJ3743】[Coci2015]Kamp 树形DP

[BZOJ3743][Coci2015]Kamp Description 一颗树n个点,n-1条边,经过每条边都要花费一定的时间,任意两个点都是联通的. 有K个人(分布在K个不同的点)要集中到一个点举行聚会. 聚会结束后需要一辆车从举行聚会的这点出发,把这K个人分别送回去. 请你回答,对于i=1~n,如果在第i个点举行聚会,司机最少需要多少时间把K个人都送回家. Input 第一行两个数,n,K. 接下来n-1行,每行三个数,x,y,z表示x到y之间有一条需要花费z时间的边. 接下来K行,每行一