题意:给定一棵无根树,每个节点有一个值, 你可以在任意两节点间划一下,使得分开的两棵子树的值的差最小。
题解:若所有节点的值的总和为奇数,那么任意划一下一定得一奇一偶的两棵子树,值之差一定是奇数且>=1。若总值为偶数,那么值之差一定为偶数且>=0。
#include <cmath>
#include <iostream>
using namespace std;
#define N 100005
#define lint __int64
#define INF 9999999999999 //开得足够大才行
int m, n, k;
int num[N];
bool vis[N];
lint dp[N], ans, sum;
struct TreeNode
{
int id;
TreeNode *next;
};
TreeNode node[N];
TreeNode store[2*N];
inline lint abl ( lint a )
{
return a < 0 ? -a : a;
}
inline lint min ( lint a, lint b )
{
return a < b ? a : b;
}
void add ( int u, int v )
{
TreeNode *p = &store[k++];
p->id = v;
p->next = node[u].next;
node[u].next = p;
}
void dfs ( int u )
{
vis[u] = 1;
dp[u] = num[u];
if ( ans < 2 ) return; // 优化一下
TreeNode *p = node[u].next;
while ( p != NULL )
{
int v = p->id;
if ( ! vis[v] )
{
dfs ( v );
dp[u] += dp[v];
if ( ans < 2 ) return;
}
p = p->next;
}
ans = min ( ans, abl(sum-dp[u]*2) );
}
int main()
{
int i, u, v, Case = 0;
while ( scanf("%d %d",&n,&m) )
{
if ( ! m && ! n ) break;
k = sum = 0;
memset(vis,0,sizeof(vis));
memset(node,NULL,sizeof(node));
memset(store,NULL,sizeof(store));
for ( i = 1; i <= n; i++ )
{
scanf("%d",&num[i]);
sum += num[i];
}
while ( m-- )
{
scanf("%d %d",&u,&v);
add ( u, v );
add ( v, u );
}
ans = INF;
dfs ( 1 );
printf("Case %d: %I64d\n", ++Case, ans );
}
return 0;
}