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
  • 相关阅读:
    201521044091《Java程序设计》第7周学习总结
    201521044091《java程序设计》第四次总结
    201521044091 《java程序设计》第八周学习总结
    201521044091 《Java程序设计》第5周学习总结
    201521044091 《Java程序设计》第2周学习总结
    201521044091 《Java程序设计》第3周学习总结
    MySQL设置字符集CHARACTER SET
    Create My MySQL configuration by Percona
    How to use jQuery to manipulate Cookies
    How to use OpenXml to import xml data to Sql server
  • 原文地址:https://www.cnblogs.com/galaxies/p/bzoj3677.html
Copyright © 2011-2022 走看看