[NOI2011]道路修建

题目传送门

考虑一条边所做的贡献,为这条边两端的树节点的数目之差的绝对值,因此我们可以搜索,随便钦定一个点为根,然后记录一下每个点子树的大小,那么对于每条边所做的贡献,就是w[i]*abs((n-siz[v[i]])-siz[v[i]]),其中i为边的编号,abs为取绝对值,siz[x]表示x的子树大小,n表示总结点数。

这道题目是树上问题中比较有代表性的一道,其本身并不难,但是可以发现,树上搜索是有效解决树上问题的途径。

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #include<cmath>
 5 #define N 5000005
 6 #define int long long
 7 using namespace std;
 8 int read()
 9 {
10     int x=0,f=1;char ch=getchar();
11     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
12     while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
13     return x*f;
14 }
15 int n,siz[N],v[N],w[N],nxt[N],head[N],cnt,ans;
16 void add(int a,int b,int c)
17 {
18     v[++cnt]=b;
19     w[cnt]=c;
20     nxt[cnt]=head[a];
21     head[a]=cnt;
22 }
23 void dfs(int x,int fa)
24 {
25     siz[x]=1;
26     for(int i=head[x];i;i=nxt[i])
27     {
28         if(v[i]==fa)continue;
29         dfs(v[i],x);
30         siz[x]+=siz[v[i]];
31         ans+=w[i]*abs(siz[v[i]]-(n-siz[v[i]]));
32     }
33 }
34 signed main()
35 {
36     n=read();
37     for(int x,y,z,i=1;i<n;i++)
38     {
39         x=read();y=read();z=read();
40         add(x,y,z);add(y,x,z);
41     }
42     dfs(1,0);
43     cout<<ans<<endl;
44     return 0;
45 }
View Code
原文地址:https://www.cnblogs.com/szmssf/p/11730125.html