蒜头君有一棵有根树,树的每一边都有边权,蒜头君想知道任意两点间最短距离之和为多少。另外,由于各种原因,蒜头君的树的边的边权会发生若干次改变,蒜头君想让你告诉他,每一次改变后,任意两点间最短距离之和为多少?
输入格式
第一行一个正整数 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);
}
}