BZOJ3743: [Coci2015]Kamp

看上去像是一道换根dp什么的

大概有点换根的意思

随便从一个目的地开始搜索,先搜出来 has[x] 表示以 x 为根的子树中有没有目的地

利用 has[] 数组继续搜,搜出 “仅包含目的地和必经点组成的树” 的大小

我们发现“最后走最长的路不回来”这样的方案是最优的,答案就是 (整棵“仅包含目的地和必经点组成的树”的大小 * 2 - 到这个节点最远的目的地的距离)

所以我们还要求出 maxl[x] 代表从 x 出发的最长链长

发现一遍搜索并不能计算出来所有点的 maxl 值

 

并且发现对于上边这张图的情况在更新 2 号节点时只记录 maxl 是没法更新出正确的 maxl 的

所以我们还需要记录 secl[x] 表示从 x 出发的次长链长

并在转移时限制一下使这两条链没有重边就可以了,只要在更新的时候每个儿子仅更新一次就好了

在第二次对每个节点更新正确的最长链的时候要这样:

ll path = edge[i].val + ((edge[i].val + maxl[y] == maxl[x]) ? (secl[x]) : (maxl[x]));
if(path >= maxl[y]) {
    secl[y] = maxl[y];
    maxl[y] = path;
} else if(path > secl[y]) secl[y] = path;

来确保父亲节点的最长链与当前节点的最长链没有重边,否则用父亲的次长链来更新

需要注意的是我们求的链一定要是以某一个目的地为结尾的,也需要在搜索时加以限制,将非目的地节点的 maxl 置为 -inf 即可,这样就不会用一个非目的地节点更新其他节点的最长链了

upd:(不过好像在 “仅包含目的地和必经点组成的树”中的直径也有点最长链的意思,毕竟它一定以叶节点为一端且这棵树中叶节点一定是目的地,解释不清是因为这题的一些细节记不起来了


 

代码:

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cctype>
#include<map>
using namespace std;
 
typedef long long ll;
const int MAXN = 500001;
 
struct EDGE{
    int nxt, to;
	ll val;
    EDGE(int NXT = 0, int TO = 0, ll VAL = 0ll) {nxt = NXT; to = TO; val = VAL;}
}edge[MAXN << 1];
int n, k, totedge, bgn;
int head[MAXN];
ll maxl[MAXN], secl[MAXN], trlen[MAXN];
bool dest[MAXN], has[MAXN];
 
inline int rd() {
    register int x = 0;
    register char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) {
        x = x * 10 + (c ^ 48);
        c = getchar();
    }
    return x;
}
inline int rdll() {
    register ll x = 0ll;
    register char c = getchar();
    while(!isdigit(c)) c = getchar();
    while(isdigit(c)) {
        x = x * 10 + (c ^ 48);
        c = getchar();
    }
    return x;
}
inline void add(int x, int y, ll v) {
    edge[++totedge] = EDGE(head[x], y, v);
    head[x] = totedge;
    return;
}
void predfs(int x, int fa) {
    bool curres = false;
    curres |= dest[x];
    for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa) {
        int y = edge[i].to;
        predfs(y, x);
        curres |= has[y];
    }
    has[x] |= curres;
    return;
}
void lendfs(int x, int fa) {
    for(int i = head[x]; i; i = edge[i].nxt) if(has[edge[i].to] && edge[i].to != fa) {
        int y = edge[i].to;
        lendfs(y, x);
        trlen[x] += trlen[y] + edge[i].val;
    }
    return;
}
void chndfs(int x, int fa) {
	if(dest[x]) maxl[x] = 0ll, secl[x] = -0x3f3f3f3f3f3fll;
	else maxl[x] = secl[x] = -0x3f3f3f3f3f3fll;
    for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa) {
        int y = edge[i].to;
        chndfs(y, x);
        ll path = edge[i].val + maxl[y];
        if(path >= maxl[x]) {
            secl[x] = maxl[x];
            maxl[x] = path;
        } else if(path > secl[x]) secl[x] = path;
    }
    return;
}
void efs(int x, int fa) {
    for(int i = head[x]; i; i = edge[i].nxt) if(edge[i].to != fa) {
        int y = edge[i].to;
        ll path = edge[i].val + ((edge[i].val + maxl[y] == maxl[x]) ? (secl[x]) : (maxl[x]));
        if(path >= maxl[y]) {
        	secl[y] = maxl[y];
        	maxl[y] = path;
        } else if(path > secl[y]) secl[y] = path;
        if(has[y]) trlen[y] = trlen[bgn];
        else trlen[y] = edge[i].val + trlen[x];
        efs(y, x);
    }
    return;
}
 
int main() {
    n = rd(); k = rd();
    register int xx, yy;
	register ll vv;
    for(int i = 1; i < n; ++i) {
        xx = rd(); yy = rd(); vv = rdll();
        add(xx, yy, vv); add(yy, xx, vv);
    }
    for(int i = 1; i <= k; ++i) {
        bgn = rd();
        dest[bgn] = true;
    }
    predfs(bgn, 0);
    lendfs(bgn, 0);
    chndfs(bgn, 0);
    efs(bgn, 0);
    for(int i = 1; i <= n; ++i) {
        printf("%lld\n", (trlen[i] << 1) - maxl[i]);
    }
    return 0;
}

  

 

posted @ 2018-08-04 11:46  EvalonXing  阅读(272)  评论(0编辑  收藏  举报