题意:给出一棵树,每个点有权值,每次操作可以对一个联通子集中的点全部加1,或者全部减1,且每次操作必须包含点1,问最少通过多少次操作可以让整棵树每个点的权值变为0.
解题关键:自底向上dp,记录up,down两个数组 代表u被加的次数和减的次数,以1为根,则
$up[u] = \max (up[v])$
$down[u] = \max (down[v])$
而子树确定,该节点改变的次数也就确定了。从而推出该点的up和down的影响,至于为什么取max,因为左右子树可以互相影响,只要包含根节点即可。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<cmath> 6 #include<iostream> 7 using namespace std; 8 typedef long long ll; 9 const int maxn=1e6+3; 10 const int inf=0x3f3f3f3f; 11 int head[maxn],tot,n,m,sum; 12 ll cnt[maxn]; 13 ll up[maxn],down[maxn]; 14 struct edge{ 15 int to; 16 int nxt; 17 int w; 18 }e[maxn<<2]; 19 void add_edge(int u,int v){ 20 e[tot].to=v; 21 e[tot].nxt=head[u]; 22 head[u]=tot++; 23 } 24 25 void dfs1(int u,int fa){ 26 for(int i=head[u];i!=-1;i=e[i].nxt){ 27 int v=e[i].to; 28 if(v==fa) continue; 29 dfs1(v,u); 30 up[u]=max(up[u],up[v]); 31 down[u]=max(down[u],down[v]); 32 } 33 cnt[u]+=up[u]-down[u]; 34 if(cnt[u]>0) down[u]+=cnt[u]; 35 else up[u]-=cnt[u]; 36 } 37 inline int read(){ 38 char k=0;char ls;ls=getchar();for(;ls<‘0‘||ls>‘9‘;k=ls,ls=getchar()); 39 int x=0;for(;ls>=‘0‘&&ls<=‘9‘;ls=getchar())x=(x<<3)+(x<<1)+ls-‘0‘; 40 if(k==‘-‘)x=0-x;return x; 41 } 42 43 int main(){ 44 int k=0; 45 while(scanf("%d",&n)!=EOF){ 46 memset(head,-1,sizeof head); 47 tot=0; 48 int a,b; 49 for(int i=0;i<n-1;i++){ 50 a=read(); 51 b=read(); 52 add_edge(a,b); 53 add_edge(b,a); 54 } 55 for(int i=1;i<=n;i++) scanf("%lld",&cnt[i]); 56 dfs1(1,-1); 57 printf("%lld\n",up[1]+down[1]); 58 } 59 return 0; 60 }
时间: 2024-12-28 20:04:29