蒜头君的树
蒜头君的树
蒜头君有一棵有根树,树的每一边都有边权,蒜头君想知道任意两点间最短距离之和为多少。另外,由于各种原因,蒜头君的树的边的边权会发生若干次改变,蒜头君想让你告诉他,每一次改变后,任意两点间最短距离之和为多少?
输入格式
第一行一个正整数 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
题解:
每一条边可以将图分为两个点集,这条边被利用的次数就是两个点集中点数之和,所以每次只需要修改一条边即可。
代码如下:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<algorithm> using namespace std; typedef long long lol; lol n,m,ans; lol head[100005],size=1; struct node{lol next,to,dis;}edge[200005]; void putin(lol from,lol to,lol dis){size++;edge[size].next=head[from];edge[size].to=to;edge[size].dis=dis;head[from]=size;} void in(lol from,lol to,lol dis){putin(from,to,dis);putin(to,from,dis);} lol fa[100005],cnt[100005],tot,dis[100001]; void dfs(lol r) { lol i; for(i=head[r];i!=-1;i=edge[i].next) { lol y=edge[i].to; if(y!=fa[r]) { dfs(y); cnt[r]+=cnt[y]; tot+=cnt[y]*(n-cnt[y])*edge[i].dis; } } cnt[r]++; return; } lol gi() { lol ans=0,f=1; char i=getchar(); while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();} while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();} return ans*f; } int main() { lol i,j; memset(head,-1,sizeof(head)); n=gi(); for(i=2;i<=n;i++) { fa[i]=gi();dis[i]=gi(); in(fa[i],i,dis[i]); } dfs(1); printf("%lld\n",tot); m=gi(); for(i=1;i<=m;i++) { lol x=gi(),y=gi(); tot+=(y-dis[x])*cnt[x]*(n-cnt[x]); printf("%lld\n",tot); dis[x]=y; } return 0; }