蒜头君的树(求树上两点距离之和+不断修改)

蒜头君有一棵有根树,树的每一边都有边权,蒜头君想知道任意两点间最短距离之和为多少。另外,由于各种原因,蒜头君的树的边的边权会发生若干次改变,蒜头君想让你告诉他,每一次改变后,任意两点间最短距离之和为多少?

输入格式

第一行一个正整数 nn,表示蒜头君的树上的结点个数。

接下来 n-1n−1 行,每行两个正整数 x_i,y_ixi​,yi​,x_ixi​表示 i+1i+1 号结点的父亲结点的编号,保证其父结点编号小于自己编号。y_iyi​ 表示 i+1i+1 号结点的父亲结点与自己间边的距离。

接下来一行一个整数 mm,表示树的边权发生改变的次数。

接下来 mm 行,每行两个正整数 a,ba,b,表示将 aa结点与其父亲结点之间的距离改为 bb,保证 a \ge 2a≥2。

输出格式

先输出一个整数,表示对于原始的树任意两点间最短距离之和。

接下来 mm 行,每行输出一个整数,表示每一次改变后,任意两点间最短距离之和。

数据规模

样例输入

4
1 1
1 1
1 1
1
2 2

样例输出

9
12

题目链接

https://nanti.jisuanke.com/t/16446

有几分莫队的意思,输入规格很好,省去了一些麻烦。

重点是sum【i】+n-sum【i】乘以边权就是总贡献

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
#define ll long long
ll fa[100005],quan[100005];
ll sum[100005];
int main()
{
    int num;
    scanf("%d",&num);
    memset(fa,0,sizeof(fa));
    memset(quan,0,sizeof(quan));
    memset(sum,0,sizeof(sum));
    for(int i=2;i<=num;i++){

        int a,b;
        scanf("%d%d",&a,&b);
        fa[i]=a,quan[i]=b;
        sum[i]=1;
    }
    int testnum;
    scanf("%d",&testnum);
    for(int i=num;i>=2;i--){
        sum[fa[i]]+=sum[i];
    }

    ll ans=0;
    for(int i=2;i<=num;i++){
      
        ans+=sum[i]*(num-sum[i])*quan[i];
    }
    printf("%lld\n",ans);
    while(testnum--){
        int aa,bb;
        scanf("%d%d",&aa,&bb);
        ans-=sum[aa]*(num-sum[aa])*quan[aa];
        quan[aa]=bb;
        ans+=sum[aa]*(num-sum[aa])*quan[aa];
        printf("%lld\n",ans);
    }
   
}

猜你喜欢

转载自blog.csdn.net/qq_41548233/article/details/82142506