码迷,mamicode.com
首页 > Windows程序 > 详细

【BZOJ 3677】 [Apio2014]连珠线

时间:2015-05-12 09:23:28      阅读:307      评论:0      收藏:0      [点我收藏+]

标签:bzoj   oi   树形dp   

3677: [Apio2014]连珠线

Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 119 Solved: 60
[Submit][Status][Discuss]
Description

在列奥纳多·达·芬奇时期,有一个流行的童年游戏,叫做“连珠线”。不出所料,玩这个游戏只需要珠子和线,珠子从1到礼编号,线分为红色和蓝色。游戏
开始时,只有1个珠子,而接下来新的珠子只能通过线由以下两种方式被加入:
1.Append(w,杪):-个新的珠子w和一个已有的珠子杪连接,连接使用红线。
2.Insert(w,u,v):-个新的珠子w加入到一对通过红线连接的珠子(u,杪)
之间,并将红线改成蓝线。也就是将原来u连到1的红线变为u连到w的蓝线与W连到V的蓝线。
无论红线还是蓝线,每条线都有一个长度。而在游戏的最后,将得到游戏的
最后得分:所有蓝线的长度总和。
现在有一个这个游戏的最终结构:你将获取到所有珠子之间的连接情况和所
有连线的长度,但是你并不知道每条线的颜色是什么。
你现在需要找到这个结构下的最大得分,也就是说:你需要给每条线一个颜
色f红色或蓝色),使得这种连线的配色方案是可以通过上述提到的两种连线方式
操作得到的,并且游戏得分最大。在本题中你只需要输出最大的得分即可。

Input

第一行是一个正整数n,表示珠子的个数,珠子编号为1刭n。
接下来n-l行,每行三个正整数ai,bi(l≤ai10000),表示有一条长度为ci的线连接了珠子ai和珠子bi。

Output

输出一个整数,为游戏的最大得分。

Sample Input

5

1 2

1 3 4 0

1 4 1 5

1 5 2 0

Sample Output

60

HINT

数据范围满足1≤n≤200000。

树形dp。

确定根之后,之后出现蓝色一定是父子孙的关系。dp一次,然后O(1)换根即可(记录最大和第二大即可)。

(这题如果直接记录三种状态不定根dp会出现不合法的:
技术分享

#include <iostream>
#include <cmath>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#define M 200005
#define inf 0x3f3f3f3f
using namespace std;
int ans,tot,h[M],n;
struct edge
{
    int y,ne,v;
}e[M*2];
struct data
{
    int k,v;
};
struct DP
{
    int v;
    data d[3];
}f[M][2];
void Addedge(int x,int y,int v)
{
    e[++tot].y=y;
    e[tot].ne=h[x];
    e[tot].v=v;
    h[x]=tot;
}
void Upd(data &a,data &b,int x,int v)
{
    if (v>=a.v)
    {
        b=a;
        a.v=v,a.k=x;
    }
    else if (v>b.v)
        b.v=v,b.k=x;
}
void dp(int x,int fa)
{
    f[x][1].d[1].v=f[x][1].d[2].v=-inf;
    f[x][0].v=0;
    for (int i=h[x];i;i=e[i].ne)
    {
        int y=e[i].y;
        if (y==fa) continue;
        dp(y,x);
        int k;
        if (f[y][1].v+f[y][1].d[1].v+e[i].v>f[y][0].v)
            k=f[y][1].v+f[y][1].d[1].v+e[i].v;
        else k=f[y][0].v;
        f[x][0].v+=k;
        f[x][1].v+=k;
        Upd(f[x][1].d[1],f[x][1].d[2],y,-k+f[y][0].v+e[i].v);
    }
}
void dfs(int x,int fa)
{
    ans=max(ans,f[x][0].v);
    for (int i=h[x];i;i=e[i].ne)
    {
        int y=e[i].y;
        if (y==fa) continue;
        DP a=f[y][0],b=f[y][1];
        int p=max(f[y][0].v,f[y][1].v+f[y][1].d[1].v+e[i].v);
        int kk=f[x][0].v-p;
        int k1;
        if (f[x][1].d[1].k==y) k1=2;
        else k1=1;
        int k=max(kk,f[x][1].v-p+f[x][1].d[k1].v+e[i].v);
        f[y][0].v+=k;
        f[y][1].v+=k;
        Upd(f[y][1].d[1],f[y][1].d[2],x,-k+kk+e[i].v);
        dfs(y,x);
        f[y][0]=a,f[y][1]=b;
    }
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<n;i++)
    {
        int x,y,v;
        scanf("%d%d%d",&x,&y,&v);
        Addedge(x,y,v),Addedge(y,x,v);
    }
    dp(1,0);
    ans=-inf;
    dfs(1,0);
    cout<<ans<<endl;
    return 0;
}

技术分享

感悟:
通过枚举根来简化dp

【BZOJ 3677】 [Apio2014]连珠线

标签:bzoj   oi   树形dp   

原文地址:http://blog.csdn.net/regina8023/article/details/45664389

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!