题意:切掉树上的某条边,使分开的两棵树上各点的权值和差值最小。
与hdu2196不同的是,此题是点权,其他无太大差别,注意数据范围。
先求出每个节点的子树权值和,然后自底向上dp即可。取$min (abs(sum - 2*dp[u]))$
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 ll inf=1e17+3; 11 ll head[maxn],tot,n,m,sum; 12 struct edge{ 13 ll to; 14 ll nxt; 15 ll w; 16 }e[maxn<<2]; 17 void add_edge(int u,int v,int w){ 18 e[tot].to=v; 19 e[tot].w=w; 20 e[tot].nxt=head[u]; 21 head[u]=tot++; 22 } 23 24 ll dp[maxn<<2],cnt[maxn<<2]; 25 26 void dfs(ll u,ll fa){ 27 for(int i=head[u];i!=-1;i=e[i].nxt){ 28 int v=e[i].to; 29 if(v==fa) continue; 30 dfs(v,u); 31 cnt[u]+=1ll*cnt[v]; 32 } 33 dp[u]=min(dp[u],sum-2*cnt[u]>=0?sum-2*cnt[u]:2*cnt[u]-sum); 34 } 35 36 inline int read(){ 37 char k=0;char ls;ls=getchar();for(;ls<'0'||ls>'9';k=ls,ls=getchar()); 38 int x=0;for(;ls>='0'&&ls<='9';ls=getchar())x=(x<<3)+(x<<1)+ls-'0'; 39 if(k=='-')x=0-x;return x; 40 } 41 42 int main(){ 43 int k=0; 44 while(scanf("%lld%lld",&n,&m)!=EOF&&(n||m)){ 45 fill(dp+1,dp+n+1,inf); 46 memset(head,-1,sizeof head); 47 tot=0; 48 sum=0; 49 int a,b; 50 for(int i=1;i<=n;i++){ 51 cnt[i]=read(); 52 sum+=1ll*cnt[i]; 53 } 54 for(int i=0;i<m;i++){ 55 a=read(); 56 b=read(); 57 add_edge(a,b,1); 58 add_edge(b,a,1); 59 } 60 printf("Case %d: ",++k); 61 dfs(1,-1); 62 ll ans=inf; 63 for(int i=1;i<=n;i++){ 64 ans=min(ans,dp[i]); 65 } 66 printf("%lld ",ans); 67 } 68 return 0; 69 }