随便讲点
本来是想所有题目做完后一次性写一篇的,没想到div.1div.1div.1那么狠,很多题虽然知道思路但总是AAA不了,总而言之还是自己太菜了。
这题是南京站的铜牌题,赛场上面感觉自己差点写出来,后来看一下其实还是有点差距的,如果当时能再多给我一个小时调代码还是有可能的。或许是因为赛场上面花了太多的时间和精力,后来补这题时候多少有点心理阴影,写了很久都写不出来。
题意
给你一棵树,这棵树上面有很多个节点,每个节点上面有很多蝴蝶,当你走到一个节点时候你将立即获得这个节点上面所有的蝴蝶,但另一方面来说,当你走到一个节点时候周围的蝴蝶将在t[i](t[i]<=3)t[i](t[i]<=3)t[i](t[i]<=3)的时间内消失。请问最多能够抓到多少蝴蝶?
题解
首先明确一些简单的定理:
- 当t[i]==3t[i]==3t[i]==3时候,我们到达一个点的父节点的时候我们还可以先去一次它的兄弟节点,然后再去它
- 当t[i]==1t[i]==1t[i]==1和t[i]==2t[i]==2t[i]==2本质相同,都只能直接向下走。
接下来是全部的分析过程
先设定两个数组sum[],f[]sum[],f[]sum[],f[],分别代表当前节点所有子节点的fff值之和以及当前子树根节点消散但子节点没有消散的情况下能够获得的最大权值。
当我们走到一个节点时候我们有两种策略
-
直接向下走
这种情况相对简单,直接给出公式f[x]=max(sum[x]+w[son])f[x]=max(sum[x]+w[son])f[x]=max(sum[x]+w[son])。
-
先试探一个子节点然后进入一个t[x]==3t[x]==3t[x]==3的节点
先给出公式f[x]=max(sum[x]−f[son1]+w[son1]+sum[son1]+w[son2])f[x]=max(sum[x]-f[son1]+w[son1]+sum[son1]+w[son2])f[x]=max(sum[x]−f[son1]+w[son1]+sum[son1]+w[son2])其中t[son2]==3,son1!=son2t[son2]==3,son1!=son2t[son2]==3,son1!=son2。
sum[x]sum[x]sum[x]:我们不同多解释;
−f[son1]+w[son1]+sum[son1]-f[son1]+w[son1]+sum[son1]−f[son1]+w[son1]+sum[son1]:被我们尝试进入son1son1son1然后我们取了它的权值惊动了它的子节点。
w[son2]w[son2]w[son2]:我们尝试进入son2son2son2并接着往下走。
但是很明显的是,这样做无疑是会时间超限的,因为我们枚举了两个不同的节点,时间复杂度可能会被卡到O(n2)O(n^2)O(n2)。
为了优化算法,我们可以选择,求出w[son2]w[son2]w[son2]的最大值和次大值,然后枚举son1son1son1,当son1==son2maxson1==son2_{max}son1==son2max时候我们选择使用次大值作为w[son2]w[son2]w[son2]。
const int N=1e5+10;
vector<vector<int>> tree;
int sum[N],f[N];
int w[N],t[N],n;
void INIT(){
tree.clear();
for(int i=0;i<=n+5;i++) sum[i]=f[i]=0;
return;
}
void dfs(int fa,int x){
int maxi1=-inf,maxi2=-inf;
int maxid1=0,maxid2=0;
for(auto v:tree[x]){
if(v==fa) continue;
dfs(x,v);
sum[x]+=f[v];
if(t[v]==3) if(w[v]>=maxi1) maxi2=maxi1,maxi1=w[v],maxid2=maxid1,maxid1=v;
else if(w[v]>maxi2) maxi2=w[v],maxid2=v;
}
for(auto v:tree[x]){
if(v==fa) continue;
f[x]=max(f[x],sum[x]+w[v]);
if(v==maxid1) f[x]=max(f[x],sum[x]-f[v]+w[v]+sum[v]+maxi2);
else f[x]=max(f[x],sum[x]-f[v]+w[v]+sum[v]+maxi1);
}
return ;
}
void MAIN(){
cin>>n;
tree.resize(n+1);
for(int i=1;i<=n;i++) cin>>w[i];
for(int i=1;i<=n;i++) cin>>t[i];
for(int i=1,u,v;i<n&&cin>>u>>v;i++) tree[u].push_back(v),tree[v].push_back(u);
dfs(-1,1);
cout<<(f[1]+w[1])<<endl;
return ;
}
AC main(){
#ifdef DEBUG
freopen("/Users/chenzhiyuan/Desktop/ACM/ACM/in.txt", "r", stdin);
freopen("/Users/chenzhiyuan/Desktop/ACM/ACM/out.txt", "w", stdout);
unsigned long start=clock();
#endif
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
INI();
int _=1;
cin>>_;
while(_--){
INIT();
MAIN();
}
#ifdef DEBUG
unsigned long end=clock();
cout<<(end-start)/1000<<"ms"<<endl;
#endif
return 0;
}