这题给我最深的感受就是:初始化很重要……我因为初始化的原因WA了一中午……
这题其实和dp并没有什么特别大的联系,其实是一个枚举。
对于截断的一条边的两个节点所在子树大小差取一个最小值就行了。
可以把这题中的树想象成一棵有根树,令第一号节点为根,然后枚举当前节点的所有子树,即枚举所有的边,然后计算答案就行了。
附上AC代码:
#include <cstdio>
#include <cstring>
#define N 100010
#define abs(a) (a)>(0)?(a):(-a)
#define min(a,b) (a)<(b)?(a):(b)
using namespace std;
struct side{
int to,nt;
}s[200010];
int n,m,x,y,num,h[N],a[N],ti;
long long c[N],sum,ans;
bool b[N];
inline void add(int x,int y){
s[++num]=(side){y,h[x]},h[x]=num;
s[++num]=(side){x,h[y]},h[y]=num;
}
inline void so(int x,int fa){
c[x]=(long long)a[x],b[x]=1;
for (int i=h[x]; i; i=s[i].nt)
if (!b[s[i].to]) so(s[i].to,x),c[x]+=c[s[i].to];
return;
}
int main(void){
while (scanf("%d%d",&n,&m),n+m){
memset(h,0,sizeof h),num=0,sum=0;
for (int i=1; i<=n; ++i) scanf("%d",&a[i]),sum+=(long long)a[i];
for (int i=1; i<=m; ++i) scanf("%d%d",&x,&y),add(x,y);
memset(b,0,sizeof b),ans=21474836470000000,so(1,0);
for (int i=1; i<=n; ++i) ans=min(ans,abs((sum-(c[i]<<1))));
printf("Case %d: %lld\n",++ti,ans);
}
return 0;
}