题目大意:
Paimon 正在树上捕捉水晶蝴蝶,这是提瓦特的一种特殊蝴蝶。树是由 n 个顶点和 (n-1) 个无向边组成的连通图。
在第 i 个顶点上最初有 ai 个晶体。当派蒙到达一个顶点时,她可以立即捕捉到该顶点上所有剩余的水晶蝇。然而,水晶蝇很胆小。当 Paimon 到达一个顶点时,所有相邻顶点上的晶体都会受到干扰。对于第i个顶点,如果顶点上的晶体在第t'秒开始时第一次受到干扰,它们将在第(t'+ti)秒结束时消失。
在第 0 秒开始时,Paimon 到达顶点 1 并在第 1 秒开始之前停留在那里。然后在接下来的每一秒开始时,她可以选择以下两种操作之一:
移动到她当前顶点的相邻顶点之一,并在下一秒开始之前停留在那里(如果目的地中的水晶蝇在那一秒结束时消失,她仍然可以捕捉到它们)。
在下一秒开始之前保持静止在她当前的顶点。
计算 Paimon 在 1010101010 秒内可以捕获的最大晶蝇数量。
输入
有多个测试用例。输入的第一行包含一个整数 T,表示测试用例的数量。对于每个测试用例:
第一行包含一个整数 n (1≤n≤105),表示顶点的数量。
第二行包含 n 个整数 a1,a2,⋯,an (1≤ai≤109),其中 ai 是第 i 个顶点上的晶体数量。
第三行包含 n 个整数 t1,t2,⋯,tn (1≤ti≤3) 其中 ti 是第 i 个顶点上的晶体在受到干扰后消失的时间。
对于接下来的 (n-1) 行,第 i 行包含两个整数 ui 和 vi (1≤ui,vi≤n),表示连接树中顶点 ui 和 vi 的边。
保证所有测试用例的n之和不超过106。
输出
对于每个测试用例,输出一行,其中包含一个整数,表示 Paimon 可以捕获的最大晶蝇数量。(来自谷歌翻译)
解法:
其实这么大的时间我们肯定是可以全部取完的,因此不用考虑时间了,所以我们应该考虑一下该如何捕捉到最多的晶蝶。而这题一看就知道肯定是要用树形dp来做,因此我们可以先开始考虑状态,,我们设立f【x】为x的子节点的所有能获得的最大晶蝶,那么我们状态转移就可以是,
f【x】=所有的子节点的f【x】+当前地方能选定的子节点的晶蝶数减去可能会损失的晶蝶数
而当前状态能怎么选定子节点呢,有两种选法,一种是遇到了一个时间为3的点,我们可以晚点去,然后先去别的点去查找有没有别的点能收,但是这样做的代价就是那个别的点的可能选中的子节点无了,这也是可能会损失的晶蝶数。第二种是一条道走到黑,不转道,这样的话就是我们只走一个点,也就不用考虑可能的子节点的子节点的损失了。
代码如下:
#include<bits/stdc++.h>
#include<vector>
typedef long long ll;
using namespace std;
typedef pair<int,int> PII;
vector<int> v[100005];
long long int a[100005];
long long int ti[100005];
long long f[100005];
bool book[100005];
long long sum[200005];
long long dfs(int x,int fa){
if(book[x]==1)//记搜,应该不用解释吧。。。
return f[x];
book[x]=1;
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(y==fa)
continue;
f[x]+=dfs(y,x);//先把底下那一层加了
}
long long int t=0;
int w=0;
long long int ma=0;
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(y==fa)
continue;
if(ti[y]==3){
if(t<a[y])
t=a[y],w=y;
}
}
ma=t;//先找3的点,然后我们找到了那个点我们就可以开始考虑有没有可能会走第一种,就是别的点走完跑到这里,之所以ma=t的原因是因为怕只走3的点更好
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(y==fa)
continue;
if(w==y)//如果这个点是我们找到的点就算了,不重复选点
continue;
ma=max(ma,max(a[y],a[y]-sum[y]+t));//是本身好还是其他点好,还有是单个点不转移好还是转移好
if(ti[y]==3&&w!=0)//如果这个点也是3,我们可能由之前那个最大的点走到这个点来
ma=max(ma,max(a[y],a[y]-sum[w]+t));
}
f[x]+=ma;//加上最大的选点
sum[x]=ma;//求出最大的选点
return f[x];
}
int main(){
int t;
scanf("%d",&t);
while(t--){//普通的输入输出
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&ti[i]);
for(int i=1;i<=n-1;i++){
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
}
long long ans=dfs(1,0)+a[1];
printf("%lld\n",ans);
for(int i=0;i<=n;i++)
v[i].clear();
for(int i=0;i<=n;i++){
sum[i]=0;
book[i]=0;
f[i]=0;
}//初始化,不用解释吧
}
}
记得在cf开c++14,不然会在第五个点t,我也不知道为啥。。