【题目】
题目描述:
你的防线成功升级,从原来的一根线变成了一棵树。这棵树有 n n n 个炮台,炮台与炮台之间有 n − 1 n-1 n−1 条隧道。
你要选择一些炮台安装哨戒炮。在第 i i i 个炮台上安装哨戒炮得到的防御力为 v i v_i vi。上次说过,哨戒炮离得太近会产生神奇的效果。具体来说,对于炮台 i i i,如果它安装了哨戒炮且和 k k k 个哨戒炮用隧道直接相连,那么其防御力会变化 k × d i k\times d_i k×di。其中 d i d_i di 为炮台 i i i 的抗干扰属性值。如果为正,干扰对其有正的作用;为负,干扰对其有负的作用;为 0 0 0,则完全不受干扰。
你的整套防线的防御力为所有哨戒炮的防御力之和。求防线的最大防御力。
输入格式:
第一行一个整数 n n n,表示炮台数量。
第二行 n n n 个整数表示 v i v_i vi。
第三行 n n n 个整数表示 d i d_i di。
接下来 n − 1 n-1 n−1 行每行两个整数描述一条隧道。
输出格式:
输出一行一个整数表示答案
样例数据:
输入
2
1 1
0 0
1 2
输出
2
提示:
对于
20
%
20\%
20% 的数据,
n
≤
20
n \le 20
n≤20。
对于
40
%
40\%
40% 的数据,
n
≤
100
n \le 100
n≤100。
对于
70
%
70\%
70% 的数据,
n
≤
5000
n \le 5000
n≤5000。
对于
100
%
100\%
100% 的数据,
n
≤
100000
n \le 100000
n≤100000。
【分析】
这是今天考的最简单的一道题啦,只不过不写 l o n g    l o n g \mathrm{long} \;\mathrm{long} longlong 就只有 0 0 0 分啦。
很明显的树形 D P \mathrm{DP} DP,用 f i , 0 / 1 f_{i,0/1} fi,0/1 表示在 i i i 的子树内,选 / / /不选 i i i 的最大防御力。
那么显然的(设 j j j 为 i i i 的儿子结点):
f i , 1 = ∑ m a x ( f j , 0 , f j , 1 + d j + d i ) + v i f_{i,1}=\sum max(f_{j,0},f_{j,1}+d_j+d_i)+v_i fi,1=∑max(fj,0,fj,1+dj+di)+vi
f i , 0 = ∑ m a x ( f j , 0 , f j , 1 ) f_{i,0}=\sum max(f_{j,0},f_{j,1}) fi,0=∑max(fj,0,fj,1)
最后的答案 a n s = m a x ( f 1 , 0 , f 1 , 1 ) ans=max(f_{1,0},f_{1,1}) ans=max(f1,0,f1,1)。
【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define ll long long
using namespace std;
int n,t,v[N],d[N];
int first[N],V[N<<1],nxt[N<<1];
ll f[N][2];
void add(int x,int y)
{
nxt[++t]=first[x];
first[x]=t,V[t]=y;
}
void dp(int x,int father)
{
int i,k;
f[x][1]=v[x],f[x][0]=0;
for(i=first[x];i;i=nxt[i])
{
k=V[i];
if(k!=father)
{
dp(k,x);
f[x][1]+=max(f[k][0],f[k][1]+d[k]+d[x]);
f[x][0]+=max(f[k][0],f[k][1]);
}
}
}
int main()
{
int x,y,i;
scanf("%d",&n);
for(i=1;i<=n;++i) scanf("%d",&v[i]);
for(i=1;i<=n;++i) scanf("%d",&d[i]);
for(i=1;i<n;++i)
{
scanf("%d%d",&x,&y);
add(x,y),add(y,x);
}
dp(1,0);
printf("%lld",max(f[1][1],f[1][0]));
return 0;
}