zoukankan      html  css  js  c++  java
  • bzoj3677 [Apio2014]连珠线

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3677

    http://uoj.ac/problem/105

    【题解】

    我们发现这样一个结论:如果把某个点作为根,那么蓝线一定是fa-x-son这种情况。

    而且一个点作为只能作为一条蓝线的中点。

    那么可以dp:

    令f[i][0/1]表示x是否为蓝线中点的max值。

    具体解释:

    1. i不是蓝线中点:那么可能从下面的所有蓝线中点连接上来取个max即可。

    2. i是蓝线中点:那么我们去掉一个连接的点,把它改为连线(就是连一条son->i的蓝线),那么i就是蓝线中点了。

    那么我们对每个根做一个dp,就是O(n^2)的,可以有50多分?

    # include <stdio.h>
    # include <string.h>
    # include <algorithm>
    // # include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    const int M = 2e5 + 10;
    const int mod = 1e9+7;
    
    # define RG register
    # define ST static
    
    int n;
    int head[M], nxt[M], to[M], w[M], tot=0;
    inline void add(int u, int v, int _w) {
        ++tot; nxt[tot] = head[u]; head[u] = tot;
        to[tot] = v; w[tot] = _w;
    }
    inline void adde(int u, int v, int _w) {
        add(u, v, _w); add(v, u, _w);
    }
    
    ll f[M][2], ans;
    
    inline void dfs(int x, int fa) {
        f[x][0] = 0, f[x][1] = -1e18;
        for (int i=head[x]; i; i=nxt[i]) {
            if(to[i] == fa) continue;
            dfs(to[i], x);
            f[x][0] += max(f[to[i]][0], f[to[i]][1] + w[i]);
            f[x][1] = max(f[x][1], -max(f[to[i]][0], f[to[i]][1] + w[i]) + f[to[i]][0] + w[i]);
        }
        f[x][1] += f[x][0];
    }
    
    int main() {
        scanf("%d", &n);
        for (int i=1, u, v, _w; i<n; ++i) {
            scanf("%d%d%d", &u, &v, &_w);
            adde(u, v, _w);
        }
        for (int i=1; i<=n; ++i) {
            dfs(i, 0);
            ans = max(ans, f[i][0]);
        }
        printf("%lld
    ", ans);
        return 0;
    }
    View Code

    我们再记录一维,表示根是否在以i为根的子树(不包含i)的子树中。

    注意到会出现这种情况:

    橙色点为我们dfs下去的根(1号点),真正的根在绿色的这个点,那么红色的点在dp的时候,如果根在绿色这个地方,那么不算蓝色的中点。(可以理解为蓝线中点是按照1号点看下去来维护的)

    那么我们加一维之后,其他仍然正常维护。

    我们令

    那么

    前两个就是上面的替换一下而已。

    后面的我重点解释下。。

    f[i,0,1]表示i不是中点,根在i的子树(不包括i)

    我们枚举真的根在的子树j,那么假设有这么一条蓝线son[j]->j->i,那么就可以推出max的前半部分:

    f[j,1,1](j目前是中点)+w[i->j](蓝线)+(f[i,0,0] - t[j])(去掉这棵子树,其他的蓝线,类比f[i,0,0])

    还有一半呢?就是一个特殊情况(上面画图的那个情况)

    无论j是不是根,都存在这个情况,所以对f[j,0,0],f[j,0,1]取max。我们同样不能再走这棵子树了,所以要加上(f[i,0,0] - t[j])。接着我们考虑要不要有这根蓝线:我们求出有蓝线的贡献T,如果小于0就不加,大于0就加啊(废话)

    那么考虑怎么求贡献:贡献就是

    (w[i->j] - t[x] + w[j->k] + f[j,0,0])这个东西。也就是说,我们有(k->i->j)这样一条线。

    那么我们对于(- t[x] + w[j->k] + f[j,0,0])维护前缀、后缀max,这样就能求出去掉j的所有k的max了。

    f[i,1,1]表示i是中点,根在i的子树(不包括i)

    前面那个max和前面一样,然后我们也是要去掉这棵子树,然后连蓝线,i->j,那么i一定是中点。

    然后就这么dp。。。

    # include <stdio.h>
    # include <string.h>
    # include <algorithm>
    // # include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    typedef long double ld;
    typedef unsigned long long ull;
    const int M = 4e5 + 10;
    const int mod = 1e9+7;
    
    # define RG register
    # define ST static
    
    int n;
    int head[M], nxt[M], to[M], w[M], tot=0;
    inline void add(int u, int v, int _w) {
        ++tot; nxt[tot] = head[u]; head[u] = tot;
        to[tot] = v; w[tot] = _w;
    }
    inline void adde(int u, int v, int _w) {
        add(u, v, _w); add(v, u, _w);
    }
    
    ll f[M][2][2], ans;
    ll t[M], pre[M], suf[M];
    int tn; ll sum;
    
    inline void dfs(int x, int fa) {
        for (int i=head[x]; i; i=nxt[i]) {
            if(to[i] == fa) continue;
            dfs(to[i], x);
        }
        sum = tn = 0;
        for (int i=head[x]; i; i=nxt[i]) {
            if(to[i] == fa) continue;
            t[to[i]] = max(f[to[i]][0][0], f[to[i]][1][0] + w[i]);
            ++tn; pre[tn] = suf[tn] = f[to[i]][0][0] + w[i] - t[to[i]];
            sum = sum + t[to[i]];
        }
        pre[0] = suf[0] = pre[tn+1] = suf[tn+1] = -1e15;
        for (int i=1; i<=tn; ++i) pre[i] = max(pre[i], pre[i-1]);
        for (int i=tn; i>=1; --i) suf[i] = max(suf[i], suf[i+1]);
        f[x][0][0] = sum;
        if(tn == 0) f[x][1][0] = -1e15;
        else f[x][1][0] = f[x][0][0] + pre[tn];
        f[x][1][1] = -1e15;
        for (int i=head[x], j=0; i; i=nxt[i]) {
            if(to[i] == fa) continue;
            ++j; ll res = max(pre[j-1], suf[j+1]);
            f[x][0][1] = max(f[x][0][1], 
            // 1: 下面的是中点
                             max(f[to[i]][1][1] + w[i] + sum - t[to[i]],
            // 2: 下面的不是中点,这个点也不算是中点(由于不是fa-x-son),但是可以和其他点连接 
                             max(f[to[i]][0][0], f[to[i]][0][1]) + sum - t[to[i]] + max(res + w[i], 0ll)));
            f[x][1][1] = max(f[x][1][1], max(f[to[i]][0][0], f[to[i]][0][1]) + sum - t[to[i]] + w[i]);
        }
    }
    
    int main() {
        scanf("%d", &n);
        for (int i=1, u, v, _w; i<n; ++i) {
            scanf("%d%d%d", &u, &v, &_w);
            adde(u, v, _w);
        }
        dfs(1, 0);
        ans = max(f[1][0][0], f[1][0][1]);
        printf("%lld
    ", ans);
        return 0;
    }
    View Code
  • 相关阅读:
    hdu 2665 划分树
    概率模型与条件随机场
    shell中各种括号的作用()、(())、[]、[[]]、{}
    今日BBC
    小贝_mysql主从复制作用以及案例
    c++ builder 版CreateAnonymousThread用法
    安卓UI适配限定符
    编译3.10内核 出现错误 “undefined reference to....&quot; 解决方法
    iptables的4表5链(未完)
    已有iptables表的查看
  • 原文地址:https://www.cnblogs.com/galaxies/p/bzoj3677.html
Copyright © 2011-2022 走看看