数据范围:
- (max(c_i)<=1,n100000)
- (max(c_i)<=5,n2000)
我太菜了!!!一分都拿不到……
数据范围1:
贪心
优先点亮(c_i=1)的点,发现顺序并不影响(神奇,自己想想吧)
数据范围2:
树形DP
(f[i][0/1],i)点先点亮自己或父亲时,点亮整棵子树的最小代价
(ans=f[1][0])
考虑转移,引入辅助数组(tmp[i])从儿子得到(i)的能量转移,儿子们的最小代价,用滚动数组完成
(f[x][0]=min{tmp[i]+max(0,d_x-i)})
(f[x][1]=min{tmp[i]+max(0,d_x-i-c_{fa})})
(i)为从儿子接受的能量,要除去多余的能量传递
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f==1?x:-x;
}
const int N1=1e5+4,N2=2004;
int n,mc,ans,d[N1],c[N1];
int f[N2][2],tmp[2][N2*5];
//f 先染自己/父亲的情况下染完子树的最小代价
//tmp 染完子树并接受儿子们j的能量传递的最小代价
vector<int>e[N1];
void dfs(int x,int fa){
int s=0,cur=0;
for(auto v:e[x]){
if(v==fa)continue;
dfs(v,x);
s+=c[v];
}
memset(tmp[0],0x3f,sizeof(tmp[0]));
tmp[0][0]=0;
for(auto v:e[x]){
if(v==fa)continue;
cur^=1;
memset(tmp[cur],0x3f,sizeof(tmp[cur]));
for(int j=0;j<=s-c[v];j++){
tmp[cur][j+c[v]]=min(tmp[cur][j+c[v]],tmp[cur^1][j]+f[v][0]);
tmp[cur][j]=min(tmp[cur][j],tmp[cur^1][j]+f[v][1]);
}
}
for(int i=0;i<=s;i++){
f[x][0]=min(f[x][0],tmp[cur][i]+max(0,d[x]-i));
f[x][1]=min(f[x][1],tmp[cur][i]+max(0,d[x]-i-c[fa]));
}
}
int main(){
n=read();
for(int i=1;i<=n;i++)d[i]=read();
for(int i=1;i<=n;i++){
c[i]=read();
mc=max(mc,c[i]);
}
for(int i=1,u,v;i<n;i++){
u=read();v=read();
e[u].push_back(v);e[v].push_back(u);
}
if(mc<=1){
for(int i=1;i<=n;i++)
if(c[i]==1){
ans+=d[i];
d[i]=0;
for(auto v:e[i])d[v]--;
}
for(int i=1;i<=n;i++)
if(d[i]>0)ans+=d[i];
cout<<ans;
return (0-0);
}
memset(f,0x3f,sizeof(f));
dfs(1,0);
cout<<f[1][0];
return (0-0);
}