[树形dp]Crystalfly 2021年ICPC南京站H

Paimon is catching crystalflies on a tree, which are a special kind of butterflies in Teyvat. A tree is a connected graph consisting of 𝑛nvertices and (𝑛−1)(n−1) undirected edges. 

Pixiv ID: 93964680

 

There are initially 𝑎𝑖ai crystalflies on the 𝑖i-th vertex. When Paimon reaches a vertex, she can catch all the remaining crystalflies on the vertex immediately. However, the crystalflies are timid. When Paimon reaches a vertex, all the crystalflies on the adjacent vertices will be disturbed. For the 𝑖i-th vertex, if the crystalflies on the vertex are disturbed for the first time at the beginning of the 𝑡′t′-th second, they will disappear at the end of the (𝑡′+𝑡𝑖)(t′+ti)-th second.

At the beginning of the 00-th second, Paimon reaches vertex 11 and stays there before the beginning of the 11-st second. Then at the beginning of each following second, she can choose one of the two operations: 

  • Move to one of the adjacent vertices of her current vertex and stay there before the beginning of the next second (if the crystalflies in the destination will disappear at the end of that second she can still catch them). 
  • Stay still in her current vertex before the beginning of the next second. 

Calculate the maximum number of crystalflies Paimon can catch in 10101010101010101010 seconds.

Input

There are multiple test cases. The first line of the input contains an integer 𝑇T indicating the number of test cases. For each test case:

The first line contains an integer 𝑛n (1≤𝑛≤1051≤n≤105) indicating the number of vertices.

The second line contains 𝑛n integers 𝑎1,𝑎2,⋯,𝑎𝑛a1,a2,⋯,an (1≤𝑎𝑖≤1091≤ai≤109) where 𝑎𝑖ai is the number of crystalflies on the 𝑖i-th vertex.

The third line contains 𝑛n integers 𝑡1,𝑡2,⋯,𝑡𝑛t1,t2,⋯,tn (1≤𝑡𝑖≤31≤ti≤3) where 𝑡𝑖ti is the time before the crystalflies on the 𝑖i-th vertex disappear after disturbed.

For the next (𝑛−1)(n−1) lines, the 𝑖i-th line contains two integers 𝑢𝑖ui and 𝑣𝑖vi (1≤𝑢𝑖,𝑣𝑖≤𝑛1≤ui,vi≤n) indicating an edge connecting vertices 𝑢𝑖uiand 𝑣𝑖vi in the tree.

It's guaranteed that the sum of 𝑛n of all the test cases will not exceed 106106.

Output

For each test case output one line containing one integer indicating the maximum number of crystalflies Paimon can catch.

Example

input

2
5
1 10 100 1000 10000
1 2 1 1 1
1 2
1 3
2 4
2 5
5
1 10 100 1000 10000
1 3 1 1 1
1 2
1 3
2 4
2 5

output

10101
10111

题意: 给出一棵有n个点的树,树上每点权值为ai,每个点还有一个暂存时间ti,当走到某点i时,其所有相邻结点开始计时,当时间达到相邻点的暂存时间后,相邻点的权值就会消失变成0,问从点1出发,能够获得的最大权值。

分析: 题目中给出了暂存时间是取自[1, 3]的,当前点全部子结点暂存时间为1或2时,从该节点出发最优情况下一定走到叶子结点然后再返回来,而如果出现了暂存时间为3的子节点,则可能先走一层其他子节点,然后返回当前节点走那个暂存时间为3的子节点。由于暂存时间为3的子节点比较特殊,所以可以预处理出来某点暂存时间为3的全部子节点。之后设状态dp[i][0]表示i点权值消失且其全部子节点权值也消失时能获得的最大权值,dp[i][1]表示i点权值消失且其全部子节点权值还在时能获得的最大权值。之后遍历树一遍,自底向上更新dp值。如果当前点不存在3秒点,那么dp[now][0] = sum(dp[son][1]),dp[now][1] = dp[now][0]+max(a[son])。如果当前点存在3秒点,那么可以先遍历第一步向哪个子节点走,最后所有情况取最大值,当确定第一步走到哪个子节点后,第二步肯定要返回父节点,第三步选择哪个3秒点其实也是固定的,肯定选择具有最大点权的那个3秒点,这里把不同3秒点的贡献列出来就很容易发现了。那么只需要再求一遍3秒点的最大值,为了处理第一步下去的点就是最大的那个3秒点,还需要维护次大的3秒点,如果第一步就走到了最大的3秒点,那就第三步肯定会走次大的3秒点。

最后注意这道题要开long long,维护最大点次大点的时候不要把逻辑写错,cf上测评机不稳定可能会TLE,最好加上快读。

具体代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#include <vector>
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;

int a[1000005], t[1000005];
vector<int> to[1000005], to2[1000005];
long long dp[1000005][2];//1:当前点死而子节点全活 0:当前点死而子节点全死 

inline void read(int &x){
   int s = 0, w = 1; char ch = getchar();
   while(ch < '0' || ch > '9'){ if(ch == '-') w = -1; ch = getchar(); }
   while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
   x = s*w;
}

inline void dfs(int now, int fa){
	long long mx = 0, sum = 0; 
	dp[now][0] = dp[now][1] = 0;
	for(int i = 0; i < to[now].size(); i++){
		int v = to[now][i];
		if(v == fa) continue;
		dfs(v, now);
		sum += dp[v][1];
		mx = max(mx, 1ll*a[v]);
	}
	dp[now][0] = sum;
	dp[now][1] = sum+mx;
	if(to2[now].size()){
		long long mx1 = -inf, mx2 = -inf, id1 = -1, id2 = -1;
		for(int i = 0; i < to2[now].size(); i++){
			int v = to2[now][i];
			if(v == fa) continue;
			if(mx1 < a[v]){
				mx2 = mx1;
				id2 = id1;
				mx1 = a[v];
				id1 = v;
			}
			else if(mx2 < a[v]){
				mx2 = a[v];
				id2 = v;
			}
		}
		mx = -inf;
		for(int i = 0; i < to[now].size(); i++){ //选择先进v
			int v = to[now][i];
			if(v == fa) continue;
			if(v == id1){
				if(id2 != -1)
					mx = max(mx, a[v]+dp[v][0]+sum-dp[v][1]+mx2);
				else
					mx = max(mx, a[v]+dp[v][0]+sum-dp[v][1]);
			}
			else
				mx = max(mx, a[v]+dp[v][0]+sum-dp[v][1]+mx1);
		}
		dp[now][1] = max(mx, dp[now][1]);
	}
}

signed main()
{
	int T;
	cin >> T;
	while(T--){
		int n;
		read(n); 
		for(int i = 1; i <= n; i++)
			read(a[i]);
		for(int i = 1; i <= n; i++){
			to[i].clear();
			to2[i].clear();
			read(t[i]);
		}
		for(int i = 1; i < n; i++){
			int u, v;
			read(u), read(v);
			to[u].push_back(v);
			to[v].push_back(u);
			if(t[v] == 3)
				to2[u].push_back(v);
			if(t[u] == 3)
				to2[v].push_back(u);
		}
		dfs(1, 0);
		printf("%lld\n", a[1]+dp[1][1]);
	}
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值