Defending Plan Support (树的边 乘 权值 和最小)

题目描述

The architectural structure of the college is strange, but the rule is that there is only one simple path between every two classrooms. Now the battle between Class A and Class F broke out. As a support staff of Class F, you have to go to every fight in time to help out. With the help of icebound, Class F know the probability of each classroom being attacked. So we define an important degree for each classroom. Now ask you to find a classroom x in n classrooms as your resting base which has the minimum F(x)F(x) = ∑ w(i) × d(x, i)d is the distance between x and i.

输入

he first line is an integer n,which is the number of classrooms.
Then n-1 lines follow. Each line has three numbers x,y,z. There is a road of meters between x and y.
The last line contains n numbers. The i‑th number w(i) is the important degree of the classroom.
2 ≤ n ≤ 5 × 105, 0 ≤ z, wi ≤ 1000,1 ≤ x, y ≤ n

输出

Output a line with an integer, representing the minimum F(x).

样例输入 Copy

5
1 2 1
2 3 1
2 4 1
3 5 6
2 3 1 8 7

样例输出 Copy

60

题意:给了n个点任意两个点之间只有直接或间接一条边进行相连。每一个点都有一个权值,想让找到一个x点,使得F(x)最小.           F(x) = ∑ w(i) × d(x, i), (wi为第i个点的权值,d(x,i)从第x个点到第i个点的距离)

思路:我们可以先根据一个点求出所有的距离,以及这个点的f(x).然后在通过转换起点,来寻找最小值。

第一图:如果我们从一号点开始找,那么我们的f(x)=(w2+w3+w4+w5)*d1 + (w3+w5)*d2 + (w4)*d3 + w5*d4;

所以我们在第一查找的时候,直接更新每一个点的权值,其权值+=后面所有点的权值和。

第二个图:我们现在已经知道了一个f(x)并且每一个点的权值也更新了,现在假设起点是p,我想要把起点转换到q点上,只需要把两点权值更新即可。那么我怎样来算总和呢?

首先已知 sum =  ... + q*d   那么现在我起点换成q这个点了  sum - q*d  + (p-q)*d (从q点到p点以后所有点要用到d的和 )

此时问题就解决了,切记,不管是哪种情况,只需要对每一个点进行一次操作即可。

代码如下:

#include<bits/stdc++.h>
#define ll long long
#define N 1000010
using namespace std;
int k,a[N],u[N],v[N],w[N],first[N],Next[N],book[N];
ll minn;
void init(int x)
{
    Next[k]=first[u[x]];
    first[u[x]]=k;
    k++;
}
ll dfs1(int x,int len)
{
    ll sum=0;
    for(int i=first[x]; i!=-1; i=Next[i])
    {
        if(book[v[i]])continue;
        book[v[i]]=1;
        sum+=dfs1(v[i],w[i]);
        a[x]+=a[v[i]];
    }
    sum+=(ll)a[x]*len;
    return sum;
}
void dfs2(int x,ll sum)
{
    minn=min(minn,sum);
    for(int i=first[x]; i!=-1; i=Next[i])
    {
        if(book[v[i]])continue;
        book[v[i]]=1;
        int p=a[x];
        int q=a[v[i]];
        a[x]=p-q;
        a[v[i]]=p;
        dfs2(v[i],sum+(ll)(p-2*q)*w[i]);
        a[x]=p;
        a[v[i]]=q;
    }
}
int main()
{
    int n;
    memset(first,-1,sizeof(first));
    scanf("%d",&n);
    k=1;
    for(int i=1; i<n; i++)
    {
        scanf("%d%d%d",&u[i],&v[i],&w[i]);
        u[i+n-1]=v[i];
        v[i+n-1]=u[i];
        w[i+n-1]=w[i];
    }
    for(int i=1;i<=2*n-2;i++)
        init(i);
    for(int i=1; i<=n; i++)
        scanf("%d",&a[i]);
    memset(book,0,sizeof(book));
    book[1]=1;
    ll sum=dfs1(1,0);
    memset(book,0,sizeof(book));
    minn=sum;
    book[1]=1;
    dfs2(1,sum);
    printf("%lld\n",minn);
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值