来源:Luogu P2986,JZOJ
题目描述
B e s s i e Bessie Bessie 正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。当然,她会选择最方便的地点来举办这次集会。
每个奶牛居住在 N ( 1 < = N < = 100 , 000 ) N(1<=N<=100,000) N(1<=N<=100,000) 个农场中的一个,这些农场由 N − 1 N-1 N−1 条道路连接,并且从任意一个农场都能够到达另外一个农场。道路i连接农场 A i A_i Ai 和 B i ( 1 < = A i < = N ; 1 < = B i < = N ) B_i(1 <= A_i <=N; 1 <= B_i <= N) Bi(1<=Ai<=N;1<=Bi<=N),长度为 L i ( 1 < = L i < = 1 , 000 ) L_i(1 <= L_i <= 1,000) Li(1<=Li<=1,000)。集会可以在 N N N 个农场中的任意一个举行。另外,每个牛棚中居住者 C i ( 0 < = C i < = 1 , 000 ) C_i(0 <= C_i <= 1,000) Ci(0<=Ci<=1,000) 只奶牛。
在选择集会的地点的时候, B e s s i e Bessie Bessie 希望最大化方便的程度(也就是最小化不方便程度)。比如选择第 X X X 个农场作为集会地点,它的不方便程度是其它牛棚中每只奶牛去参加集会所走的路程之和,(比如,农场i到达农场 X X X 的距离是 20 20 20,那么总路程就是 C i ∗ 20 C_i*20 Ci∗20)。帮助 B e s s i e Bessie Bessie 找出最方便的地点来举行大集会。
Consider a country with five barns with [various capacities] connected by various roads of varying lengths. In this set of barns, neither barn 3 nor barn 4 houses any cows.
1 3 4 5
@–1--@–3--@–3--@[2]
[1] |
2 | @[1] 2 Bessie can hold the Gathering in any of five barns; here is the table of inconveniences calculated for each possible location:
Gather ----- Inconvenience ------
Location B1 B2 B3 B4 B5 Total
1 0 3 0 0 14 17
2 3 0 0 0 16 19
3 1 2 0 0 12 15
4 4 5 0 0 6 15
5 7 8 0 0 0 15
If Bessie holds the gathering in barn 1, then the inconveniences from each barn are:
Barn 1 0 – no travel time there!
Barn 2 3 – total travel distance is 2+1=3 x 1 cow = 3 Barn 3 0 – no cows there!
Barn 4 0 – no cows there!
Barn 5 14 – total travel distance is 3+3+1=7 x 2 cows = 14 So the total inconvenience is 17.
The best possible convenience is 15, achievable at by holding the Gathering at barns 3, 4, or 5.
解题思路
- 这道题是一道树形 d p dp dp,先 d f s dfs dfs 一遍,用 s i z [ x ] siz[x] siz[x] 存储以 x x x 为根的子树的节点数量, f [ i ] f[i] f[i] 表示以 i i i 为根的子树到根的距离之和,那么 f [ 1 ] f[1] f[1] 就是所有奶牛到 1 1 1 点的距离之和了;
- 然后就要利用到换根思想, d i s [ y ] = 1 L L ∗ d i s [ x ] − s i z [ y ] ∗ e [ i ] . v + ( s u m − s i z [ y ] ) ∗ e [ i ] . v ; dis[y]=1LL*dis[x]-siz[y]*e[i].v+(sum-siz[y])*e[i].v; dis[y]=1LL∗dis[x]−siz[y]∗e[i].v+(sum−siz[y])∗e[i].v;
- 想想为什么,因为当根节点换成了 y y y 时, y y y 子树下的点就少走了当前这条连接 x x x 与 y y y 的边,然而除去 y y y 子树的另外点,又要多走这一条边。(也许有些抽象)
Code
#include <bits/stdc++.h>
using namespace std;
int n,t=0;
long long sum=0,ans=1e15,dis[200010],siz[200010],f[200010],c[200010],linkk[200010];
struct node
{
long long y,v,next;
}e[200010];
void insert(int x,int y,int v) //邻接表插入
{
e[++t].y=y; e[t].v=v;
e[t].next=linkk[x]; linkk[x]=t;
}
void dfs(int x,int father) //深度优先搜索dfs
{
siz[x]=c[x]; //siz[x]表示以x为根的子树的节点数量
for (int i=linkk[x];i;i=e[i].next) //邻接表遍历
{
int y=e[i].y;
if (y!=father)
{
dfs(y,x);
siz[x]+=siz[y]; //把y为根的节点数加到以x为根的节点数中
f[x]=f[x]+f[y]+siz[y]*e[i].v; //f[i]表示以i为根的子树到根的距离之和
}
}
}
void treedp(int x,int father)
{
for (int i=linkk[x];i;i=e[i].next)
{
int y=e[i].y;
if (y!=father)
{
dis[y]=1LL*dis[x]-siz[y]*e[i].v+(sum-siz[y])*e[i].v; //换根思想
ans=min(ans,dis[y]);
treedp(y,x);
}
}
}
int main()
{
freopen("gather.in","r",stdin);
freopen("gather.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%lld",&c[i]);
sum+=c[i];
}
for (int i=1;i<n;i++)
{
int x,y,v;
scanf("%d %d %d",&x,&y,&v);
insert(x,y,v);
insert(y,x,v);
}
dfs(1,0);
dis[1]=f[1]; //聚集到点1的花费
ans=min(ans,dis[1]);
treedp(1,0);
printf("%lld",ans);
return 0;
}