2022 China Collegiate Programming Contest (CCPC) Guilin Site - G. Group Homework 换根dp

G. Group Homework

time limit per test 3 second
memory limit per test 512 megabytes
inputstandard input
outputstandard output

题目描述
No, we don’t want group homework. It’s the place where 1+1<1 can be true. However, you are currently the leader of a group with three members. Luckily, your group members will write everything down for you since you are the prestigious leader. Unluckily, you have to assign the new algorithm homework to your two team members, Mr. Dian and Mr. Beng, who can’t understand the ideas of each other correctly and mess up all the details in the cooperation.

The new homework is about a tree: there are n vertices on the tree with n−1 bidirectional edges connecting them. Each vertex i is a problem with a score of ai. You can assign a tree path to each member, then Mr. Dian and Mr. Beng will solve the problems on their path independently. The final score of the homework is decided by the sum of the scores of the vertices visited by exactly one member — as we mentioned before, if both of them solved one problem independently, they will argue a lot about who is right, and all the effort will be in vain.

Now, you — Mr. Ji — want to maximize the final score (as well as the chance not to drop out of school due to too low GPA). Make a wise choice!

输入描述

The first line of input contains a single integer n(1≤n≤2×105), denoting the number of vertices.

The second line contains n integers separated by spaces, the i-th integer denotes ai(1≤ai≤104).

In the following n−1 lines, each contains two integers u,v(1≤u,v≤n), denoting (u,v) is a tree edge.

It is guaranteed that the input edges form a tree of n vertices.

输出描述

Print an interger in a single line, denoting the maximum score if assigning paths optimally.

题意

求树上两条链 其不相交部分的最大权值和

思路

这两条链可分成两种形态
i)两条链不相交
ii)两条链交于一个点
因为若两条链交于多个点可以通过拆分构成两条不相交或相交在一点的链
ii)形态证明
那么我们只需要对这两种情况求出最值取最大即可

i)对于这种情况 那和这个板题一致 F. The Chocolate Spree(即代码中dfs处理的部分)

ii) 对于第二种情况我们只需要维护每个点作为根节点时其属的最大的4条链

而对于每个点为端点的最长链在第一种状态中已经维护好了 故我们可以直接得知以1为根的1的最大4条链
我们只需要从1开始跑换根dp即可

由于父节点于其原本的链可能会在换根后会成为子节点的最大4条链之一
故我们需要维护父节点除去to节点的影响后的最大链并加上这个父节点的权值a[at]构成的新链

对于每个节点维护4个的最大值 若to节点是这4个值中最大的链即父节点除去to节点后只能通过 m x [ a t ] [ 2 ] mx[at][2] mx[at][2]即次大值去构成一条新链
否则就可以通过 m x [ a t ] [ 1 ] mx[at][1] mx[at][1]即最大值去构成一条新链
故我们需要通过判断子节点to构成的链是否是父节点at的最大值 分类维护fmx即可

具体看注释吧!

Code

#include<bits/stdc++.h>
using namespace std;
#define __T int csT;scanf("%d",&csT);while(csT--)
const int mod=1e9+7;
const int maxn=2e5+3;

int n,u,v,cnt;
int a[maxn],h[maxn];
struct node{
    int to,nx;
}e[2*maxn];
long long dp[maxn][5];
multiset<long long> lis[maxn];
long long mx[maxn][5];
long long mx4;

//dp[u][0]:u为根节点子树中两条不相交链的-最大和
//dp[u][1]:u为根节点子树中最大的一条链(不一定要经过u)
//dp[u][2]:u为根节点子树中一条由u到叶子节点的链 加上一条与之不相交的链的-最大和
//dp[u][3]:u为根节点子树中的一条 不经过u 的-最大链
//dp[u][4]:u为根节点子树中一条由u到叶子节点的-最大链

void dfs(int at,int f)
{
    dp[at][0]=dp[at][1]=dp[at][2]=dp[at][4]=a[at];
    dp[at][3]=0;
    for(int i=h[at];~i;i=e[i].nx)
    {
        int to=e[i].to;
        if(to==f)continue;
        //由于有些状态转移不能在自己的一些状态转移之后转移 故需要注意转移状态的顺序
        dfs(to,at);

        dp[at][0]=max(dp[at][0],dp[to][0]);//继承to的两条不相交链
        dp[at][0]=max(dp[at][0],dp[at][4]+dp[to][2]);//通过加一条at-to边 拼接到at到叶子节点的链和to到叶子节点的链 和to上另一条不与之相交的链构成的两条不相交链
        dp[at][0]=max(dp[at][0],dp[at][2]+dp[to][4]);//通过加一条at-to边 拼接到at到叶子节点的链和to到叶子节点的链 和at上另一条不与之相交的链构成的两条不相交链
        dp[at][0]=max(dp[at][0],dp[at][1]+dp[to][1]);//一条at子树中的最大链 加上to子树中的最大链 ps:此时dp[at][1]还未通过to去更新故不包含to的链

        dp[at][1]=max(dp[at][1],dp[to][1]);//继承子节点to的最大链
        dp[at][1]=max(dp[at][1],dp[to][4]+dp[at][4]);//通过加一条at-to边 拼接到at到叶子节点的链和to到叶子节点的链

        dp[at][2]=max(dp[at][2],dp[to][2]+a[at]);//通过加一条at-to边 延长at点到to到叶子节点的链 和to上另一条不与之相交的链构成的两条不相交链
        dp[at][2]=max(dp[at][2],dp[at][4]+dp[to][1]);//由at到叶子节点的一条链 和 to节点子树中的一条最大链
        dp[at][2]=max(dp[at][2],dp[at][3]+dp[to][4]+a[at]);//由at子树中一条不经过at的链 和 通过加一条at-to边 延长at点到to到叶子节点的链

        dp[at][3]=max(dp[at][3],dp[to][1]);//to的子树中的最大链

        dp[at][4]=max(dp[at][4],dp[to][4]+a[at]);//通过加一条at-to边 延长at点到to到叶子节点的链
    }
}
void dfs4(int at,int f,long long fmx)//fmx维护父节点除at节点外最长链加上a[f]的值
{
    mx[at][1]=mx[at][2]=mx[at][3]=mx[at][4]=0;
    for(int i=h[at];~i;i=e[i].nx)
    {
        int to=e[i].to;
        if(to==f)continue;
        if(dp[to][4]>=mx[at][1])
        {
            mx[at][4]=mx[at][3];
            mx[at][3]=mx[at][2];
            mx[at][2]=mx[at][1];
            mx[at][1]=dp[to][4];
        }
        else if(dp[to][4]>=mx[at][2])
        {
            mx[at][4]=mx[at][3];
            mx[at][3]=mx[at][2];
            mx[at][2]=dp[to][4];
        }
        else if(dp[to][4]>=mx[at][3])
        {
            mx[at][4]=mx[at][3];
            mx[at][3]=dp[to][4];
        }
        else if(dp[to][4]>=mx[at][4])
        {
            mx[at][4]=dp[to][4];
        }
    }
    if(fmx>=mx[at][1])
    {
        mx[at][4]=mx[at][3];
        mx[at][3]=mx[at][2];
        mx[at][2]=mx[at][1];
        mx[at][1]=fmx;
    }
    else if(fmx>=mx[at][2])
    {
        mx[at][4]=mx[at][3];
        mx[at][3]=mx[at][2];
        mx[at][2]=fmx;
    }
    else if(fmx>=mx[at][3])
    {
        mx[at][4]=mx[at][3];
        mx[at][3]=fmx;
    }
    else if(fmx>=mx[at][4])
    {
        mx[at][4]=fmx;
    }

    mx4=max(mx4,mx[at][1]+mx[at][2]+mx[at][3]+mx[at][4]);

    for(int i=h[at];~i;i=e[i].nx)
    {
        int to=e[i].to;
        if(to==f)continue;
        if(dp[to][4]==mx[at][1])
        {
            dfs4(to,at,max(mx[at][2],fmx)+a[at]);
        }
        else
        {
            dfs4(to,at,max(mx[at][1],fmx)+a[at]);
        }
    }
}

inline void sol()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
    }
    if(n==1)
    {
        puts("0");
        return;
    }//特判1个节点
    memset(h,-1,sizeof h);
    for(int i=1;i<n;++i)
    {
        scanf("%d%d",&u,&v);
        e[++cnt].to=v;
        e[cnt].nx=h[u];
        h[u]=cnt;
        e[++cnt].to=u;
        e[cnt].nx=h[v];
        h[v]=cnt;
    }
    dfs(1,0);
    dfs4(1,0,0);
    printf("%lld\n",max(dp[1][0],mx4));
}

int main()
{
    //__T
    sol();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柯西可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值