3677: [Apio2014]连珠线

本文介绍了一个经典的图论问题——Apio2014连珠线,探讨了如何通过树形DP算法求解最大得分。文章详细解释了问题背景、输入输出格式、样例及数据范围,并提供了具体的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3677: [Apio2014]连珠线

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 320   Solved: 194
[ 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 10
1 3 40
1 4 15
1 5 20

Sample Output

60



HINT

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

Source

[ Submit][ Status][ Discuss]

考虑红边变成蓝边的过程,假设在给定的树上a,b,c三点依次相连
先用红线连接a,c,然后换上蓝线a,b和b,c
在之后,其他操作显然和这两条蓝线没有任何关系了
所以,对于任意合法方案,每一条蓝线总能找到唯一的一条蓝线与之对应,并且这两条蓝线有公共点
那么就直接树形dp,f[i][o]记录以i为根的子树,i向父亲的连边是否已经匹配,最优方案
维护最大值次大值什么的,讨论下换根操作就行了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
 
const int maxn = 2E5 + 20;
const int INF = ~0U>>1;
 
struct E{
    int to,w; E(){}
    E(int to,int w): to(to),w(w){}
};
 
int n,Ans,f[maxn],g[maxn],fr[maxn],pos[maxn],sc[maxn];
 
vector <E> v[maxn];
 
void Dfs1(int x,int va,int fa)
{
    fr[x] = sc[x] = -INF;
    for (int i = 0; i < v[x].size(); i++)
    {
        E e = v[x][i]; if (e.to == fa) continue;
        Dfs1(e.to,e.w,x); g[x] += max(f[e.to],g[e.to]);
    }
    for (int i = 0; i < v[x].size(); i++)
    {
        E e = v[x][i]; if (e.to == fa) continue;
        int now = g[x] - max(f[e.to],g[e.to]);
        f[x] = max(f[x],now + g[e.to] + e.w + va);
        now = g[e.to] - max(g[e.to],f[e.to]) + e.w;
        if (now < fr[x]) sc[x] = max(sc[x],now);
        else sc[x] = fr[x],fr[x] = now,pos[x] = i;
    }
}
 
void Dfs2(int x,int va,int sf,int sg,int fa)
{
    Ans = max(Ans,g[x] + max(sf,sg));
    for (int i = 0; i < v[x].size(); i++)
    {
        E e = v[x][i];
        if (e.to == fa) continue; int nf = 0,ng;
        ng = g[x] - max(f[e.to],g[e.to]) + max(sf,sg);
        if (va) nf = ng - max(sf,sg) + sg + va + e.w;
        if (pos[x] == i) nf = max(nf,ng + sc[x] + e.w);
        else nf = max(nf,ng + fr[x] + e.w);
        Dfs2(e.to,e.w,nf,ng,x);
    }
}
 
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    n = getint();
    for (int i = 1; i < n; i++)
    {
        int x = getint(),y,w;
        y = getint(); w = getint();
        v[x].push_back(E(y,w));
        v[y].push_back(E(x,w));
    }
    Dfs1(1,0,0); Dfs2(1,0,0,0,0);
    cout << Ans << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值