蔬菜(vegetable)

蔬菜(vegetable)

题目描述

 

题目背景:您使用脚本刷出了上题游戏 998244353 关的最高分 (最优解),心满意足的准备点继续学习,忽然一条弹窗弹了出来:你想明白活着的意义吗?你想真正的...... 活着吗?YES or NO 作为一名新时代新青年,您当然不信这种扯淡的东西,毫不犹豫点击了 YES,于是当您醒来的时候,您已经在一片未知森林里了...

 

您正站在森林中最显眼的一棵树前,一条叫 Skqliao 的人正在树上打盹,他告诉您这棵树是一棵无根树,在第 i 个点上有 x 颗蔬菜,如果您想要回去的话就需要收集尽量多的蔬菜。当然乐于助人的 Skqliao 也会帮助您。具体来讲,首先你和 Skqliao 各选择一个不同的起点,接着轮流选定一个与自己相邻的且两人都未经过的点并到达该点。当某人无法移动时,另一人可以继续移动,直到两人都无法移动为止。当你或 Skqliao 经过某点时就可以收集该点所有蔬菜,请你制定合理策略 (包括 Skqliao 选择的初始位置和操作方式) 尝试获得最多的蔬菜。

 

输入

 

第一行一个整数nn,表示树的点数。

第二行有nn个整数,表示每个点上的蔬菜颗数xixi。

接下来n−1n−1行,每行两个整数u,vu,v,表示uu和vv之间有一条边。

 

输出

 

能获得的最多的蔬菜数量。

 

样例输入

<span style="color:#333333"><span style="color:#333333">6
10 8 6 4 2 1
1 2
1 3
2 4
2 5
2 6</span></span>

样例输出

<span style="color:#333333"><span style="color:#333333">30</span></span>

提示

 

对于30% 的数据,n≤15n≤15

对于50% 的数据,n≤100n≤100

对于70% 的数据,n≤5000n≤5000

另有10% 的数据树的形态为一条链

对于100% 的数据,n≤200000,0≤xi≤998244353n≤200000,0≤xi≤998244353

 

来源

noip2018模拟-北京十一


solution

暴力卡过。

寻找正解请移步神犇博客(?)

可以先树形dp出每一个子树内的最长链

我们暴力枚举一个点,强制令一个点就在这棵树内

那么要修改的dp值为它到根

暴力改 效率O(n*平均深度)

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 200005
#define ll long long
using namespace std;
int n,head[maxn],tot,t1,t2,ft[maxn],flag[maxn],ban,pd;
ll s[maxn],f[maxn],l[maxn],F[maxn],L[maxn],ans;
struct node{
    int v,nex;
}e[maxn*2];
void lj(int t1,int t2){
    e[++tot].v=t2;e[tot].nex=head[t1];head[t1]=tot;
}
void dfs(int k,int fa){
    ll m1=0,m2=0,ma=0;ft[k]=fa;
    for(int i=head[k];i;i=e[i].nex){
        if(e[i].v==fa)continue;
        dfs(e[i].v,k);
        if(l[e[i].v]>m1)m2=m1,m1=l[e[i].v];
        else if(l[e[i].v]>m2)m2=l[e[i].v];
        ma=max(ma,f[e[i].v]);
    }
    l[k]=m1+s[k];f[k]=max(ma,m1+m2+s[k]);
}
void dp(int k){
    ll m1=0,m2=0,ma=0;
    for(int i=head[k];i;i=e[i].nex){
        if(e[i].v==ft[k])continue;
        if(flag[e[i].v])dp(e[i].v);
        if(e[i].v==ban)continue;
        if(!flag[e[i].v]){
            if(l[e[i].v]>m1)m2=m1,m1=l[e[i].v];
            else if(l[e[i].v]>m2)m2=l[e[i].v];
            ma=max(ma,f[e[i].v]);
        }
        else {
            if(L[e[i].v]>m1)m2=m1,m1=L[e[i].v];
            else if(L[e[i].v]>m2)m2=L[e[i].v];
            ma=max(ma,F[e[i].v]);
        }
    }
    L[k]=m1+s[k],F[k]=max(ma,m1+m2+s[k]);
}
int main()
{
    freopen("vegetable.in","r",stdin);
    freopen("vegetable.out","w",stdout);
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%lld",&s[i]);
    for(int i=1;i<n;i++){
        scanf("%d%d",&t1,&t2);
        lj(t1,t2);lj(t2,t1);
        if(t1!=t2+1&&t2!=t1+1)pd=1;
    }
    if(!pd){
        for(int i=1;i<=n;i++)ans+=s[i];
        cout<<ans<<endl;return 0;
    }
    dfs(1,0);
    for(int b=2;b<=n;b++){
        for(int i=ft[b];i;i=ft[i])flag[i]=1;
        ban=b;dp(1);
        ans=max(ans,f[b]+F[1]);
        for(int i=ft[b];i;i=ft[i])flag[i]=F[i]=L[i]=0;
    }
    cout<<ans<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值