• 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
    ", (trlen[i] << 1) - maxl[i]);
        }
        return 0;
    }
    

      

    禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载 ,用户转载请注明出处:https://www.cnblogs.com/xcysblog/
  • 相关阅读:
    阿里云ECS linux通过rinetd 端口转发来访问内网服务
    阿里云ECS linux通过iptables 配置SNAT代理网关,实现局域网上网
    适用于CentOS6.x系统的15项优化脚本
    ELK学习笔记
    MAC OSX环境下cordova+Ionic的安装配置
    Windows下 VM12虚拟机安装OS X 10.11 和VM TOOLS
    cordova 下载更新
    android adb常用命令
    ionic实现双击返回键退出功能
    ionic ngCordova插件安装
  • 原文地址:https://www.cnblogs.com/xcysblog/p/9418137.html
Copyright © 2020-2023  润新知