2021 ICPC 南京 H-Crystalfly(树上DP)

2021 ICPC 南京 H-Crystalfly(树上DP)

Probelm Statement

你一开始处于\(1\)号节点的树, 每个点\(a_i\)个蝴蝶, 且如果\(i^{th}\)节点的父节点被扰动时, 这些蝴蝶会在\(t_i(1\leq t_i\leq 3)\)时刻后消散. 求你可以抓到多少只蝴蝶.

Solution

当我们第一次到达节点\(u\)的时候它的所有儿子都会被激活,对于\(u\)的子节点\(v\), 我们只有两个决策, 要么到达子节点\(v\)知乎继续向下, 那么\(u\)的其他子节点上的蝴蝶都无法再次抓取. 或者我们进入某个子节点\(v_1(t_{v_1}=3)\)后立刻返回节点\(u\)挑另一个子节点\(v_2\)直接走下去, 然后除了\(v_1,v_2\)以外\(u\)的其他子节点上的蝴蝶都会消失.

我们不妨设:

  • \(f_i\): 我们取节点\(i\), 可以取\(i\)子节点上的蝴蝶可以收获的最大蝴蝶数.
  • \(g_i\): 我们不取节点\(i\)上的蝴蝶, 可取\(i\)子节点上的蝴蝶的最大蝴蝶数.
  • \(h_i\): 我们节点\(i\)上的蝴蝶, 但是不取\(i\)子节点上的蝴蝶可以收获的最大蝴蝶数.

对于\(h_u\)根据上述定义我们可以得到, \(h_u=\sum\limits_{v\in\text{son}u}g_v+a_u\).

由于\(f_u\)和\(g_u\)的区别主要在于节点\(u\)上的蝴蝶去不去得到, 我们有\(f_u=g_u+a_u\).

接下来我们考虑计算\(g_u\)根据上述两种两种决策:

第一种决策, 我们在节点\(u\)到达子节点\(v\)后一直往下, 节点\(u\)的其他子节点的蝴蝶都无法取到, 有转移方程式子: \(g_u=\max\{f_{v_1}+\sum\limits_{v_2\in\text{son}_u\and v_2\neq v_1}g_{v_2}\}=\sum\limits_{v\in\text{son}_u}g_v+\max\limits_{v\in\text{son}_v}\{a_v\}(v_1\in\text{son} _u)\).

第二种情况, 我们到达一个节点\(v_1\)后立刻返回, 然后选择一个节点\(v_2(t_{v_2}=3)\)一直往下, 其他子节点都无法取到, 我们有状态转移方程: \(g_u=\max\{f_{v_1}+h_{v_2}+\sum\limits_{v_3\in\text{son}_u\and v_3\neq v_2\and v_3\neq v_1}g_{v_3}\}=\max\{a_{v_1}+h_{v_2}+\sum\limits_{v_3\in\text{son}_u\and v_3\neq v_1\and v_3\neq v_2}g_{v_3}\}(v_1,v_2,v_3\in\text{son}_u,t_{v_1}=3)\). 对于情况\(2\)显然我们可以枚举\(v_1\)和\(v_2\)时间复杂度是\(O(n^2)\)的无法通过所有测试数据. 我们考虑维护\(a_{v_2}\)的最大值\(Max_1\)和次大值\(Max_2\), 然后扫一遍\(u\)的所有子节点\(v\). 如果\(t_{v}=3\and a_v=Max_1\)那么我们就计算\(g_u=max(g_u,h_v+\sum\limits_{v_1\in\text{son}_u\and v_1\neq v}g_{v_1}+Max_2)\)否则计算: \(g_u=max(g_u,h_v+\sum\limits_{v_1\in\text{son}_u\and v_1\neq v}g_{v_1}+Max_1)\). 由于\(\sum\limits_{v\in\text{son}_u}g_v\)可以\(O(\deg_u)\)预处理的, 所以上述转移式子在一个节点点\(u\)的复杂度为\(O(\deg_u)\).

上述算法的总复杂度为\(O(\sum_{i=1}^n\deg_i)=O(n)\).

Code

# define Fast_IO std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
# include "unordered_map"
# include "algorithm"
# include "iostream"
# include "cstdlib"
# include "cstring"
# include "cstdio"
# include "vector"
# include "bitset"
# include "queue"
# include "cmath"
# include "map"
# include "set"
 
using namespace std;
 
const int maxm=1e5+10;
 
int Task,N;
long long A[maxm];
int T[maxm];
long long F[maxm],G[maxm],H[maxm];

vector<int> Edge[maxm];
 
void Dfs(int Now,int Father){
	long long Max1,Max2,Max; Max=Max1=Max2=0;
	long long Res=0;
	for(auto To:Edge[Now]){
		if(To==Father) continue;
		Dfs(To,Now);
	}H[Now]=A[Now];
	for(auto To:Edge[Now]){
		if(To==Father) continue;
		H[Now]+=G[To];
	}
	for(auto To:Edge[Now]){
		if(To==Father) continue;
		Res+=G[To];
		Max=max(A[To],Max);
	}G[Now]=Res+Max;
	Max1=Max2=0;
	for(auto To:Edge[Now]){
		if(To==Father || T[To]!=3) continue;
		if(A[To]>=Max1) Max2=Max1,Max1=A[To];
		else Max2=max(Max2,A[To]);
	}
	for(auto To:Edge[Now]){
		if(To==Father) continue;
		if(A[To]!=Max1 || T[To]!=3) G[Now]=max(G[Now],Res-G[To]+H[To]+Max1);
		else G[Now]=max(G[Now],Res-G[To]+H[To]+Max2);
	}
	F[Now]=G[Now]+A[Now];
	return;
}
 
inline void Solve(){
	static int i,U,V;
	scanf("%d",&N);
	for(i=1;i<=N;i++) Edge[i].clear(),F[i]=G[i]=false;
	for(i=1;i<=N;i++) scanf("%lld",&A[i]);
	for(i=1;i<=N;i++) scanf("%d",&T[i]);
	for(i=1;i< N;i++){
		scanf("%d%d",&U,&V);
		Edge[U].push_back(V);
		Edge[V].push_back(U);
	}Dfs(1,1);
	printf("%lld\n",F[1]);
	return;
}
 
int main(){
	scanf("%d",&Task);
	while(Task--) Solve();
	return 0;
} 

Orz dls

算法来源: Namomo Camp Day1.

Codeforces Gym: The 2021 ICPC Asia Nanjing Regional Contest(XXII Open Cup,Grand Prix of Nanjing).

Vjudge: Namomo Spring Camp 2022 Div1 Week1 A.

上一篇:油田滚子链行业调研报告 - 市场现状分析与发展前景预测


下一篇:ja_JavaScript_生成器