【2019/03/30测试T2】哨戒炮 II

【题目】

传送门

题目描述:

你的防线成功升级,从原来的一根线变成了一棵树。这棵树有 n n 个炮台,炮台与炮台之间有 n 1 n-1 条隧道。

你要选择一些炮台安装哨戒炮。在第 i i 个炮台上安装哨戒炮得到的防御力为 v i v_i 。上次说过,哨戒炮离得太近会产生神奇的效果。具体来说,对于炮台 i i ,如果它安装了哨戒炮且和 k k 个哨戒炮用隧道直接相连,那么其防御力会变化 k × d i k\times d_i 。其中 d i d_i 为炮台 i i 的抗干扰属性值。如果为正,干扰对其有正的作用;为负,干扰对其有负的作用;为 0 0 ,则完全不受干扰。

你的整套防线的防御力为所有哨戒炮的防御力之和。求防线的最大防御力。

输入格式:

第一行一个整数 n n ,表示炮台数量。

第二行 n n 个整数表示 v i v_i

第三行 n n 个整数表示 d i d_i

接下来 n 1 n-1 行每行两个整数描述一条隧道。

输出格式:

输出一行一个整数表示答案

样例数据:

输入
2
1 1
0 0
1 2

输出
2

提示:

对于 20 % 20\% 的数据, n 20 n \le 20
对于 40 % 40\% 的数据, n 100 n \le 100
对于 70 % 70\% 的数据, n 5000 n \le 5000
对于 100 % 100\% 的数据, n 100000 n \le 100000


【分析】

这是今天考的最简单的一道题啦,只不过不写 l o n g    l o n g \mathrm{long} \;\mathrm{long} 就只有 0 0 分啦。

很明显的树形 D P \mathrm{DP} ,用 f i , 0 / 1 f_{i,0/1} 表示在 i i 的子树内,选 / / 不选 i i 的最大防御力。

那么显然的(设 j j 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

f i , 0 = m a x ( f j , 0 , f j , 1 ) f_{i,0}=\sum max(f_{j,0},f_{j,1})

最后的答案 a n s = m a x ( f 1 , 0 , f 1 , 1 ) ans=max(f_{1,0},f_{1,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;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/88911048
ii