蒜头君的树
2017-09-07
题目描述
蒜头君有一棵有根树,树的每一边都有边权,蒜头君想知道任意两点间最短距离之和为多少。另外,由于各种原因,蒜头君的树的边的边权会发生若干次改变,蒜头君想让你告诉他,每一次改变后,任意两点间最短距离之和为多少?
输入格式
第一行一个正整数 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
一道裸的dfs的题,不要看到树就害怕,当时模拟这个题的时候我看到树,一想就不会,现在看来,一条边的贡献就是左边点数*右边点数*w
只要单dfs一边找到一条边的孩子有多少就可以知道右边有多少....水题
#include<cstdlib> #include<cstdio> #include<iostream> #include<algorithm> #define ll long long const ll maxn=100000+9999; using namespace std; ll read(){ ll an=0,f=1;char ch=getchar(); while(!('0'<=ch&&ch<='9')){if(ch=='-')f=-f;ch=getchar();} while('0'<=ch&&ch<='9'){an=an*10+ch-'0';ch=getchar();} return an*f; } ll f[maxn],w[maxn<<1],cnt,vis[maxn],size[maxn],n,m; ll ans; struct saber{ ll nex,to; }b[maxn<<1]; void add(ll x,ll y,ll wi){ cnt++; b[cnt].nex=f[x]; b[cnt].to=y; w[cnt]=wi; f[x]=cnt; } void dfs(ll x){ vis[x]=1;size[x]=1; for(ll i=f[x];i;i=b[i].nex){ ll v=b[i].to; if(!vis[v]){ vis[v]=1; dfs(v); size[x]+=size[v]; } } } int main(){ n=read(); for(ll i=2;i<=n;i++){ ll x,y;x=read();y=read(); add(x,i,y); } dfs(1); m=read(); for(ll i=1;i<n;i++){ ans+=w[i]*(n-size[b[i].to])*(size[b[i].to]); } cout<<ans<<endl; while(m){m--; ll x,y,z;x=read();y=read(); z=w[x-1]; w[x-1]=y; y=y-z; ans+=y*(n-size[b[x-1].to])*size[b[x-1].to]; cout<<ans<<endl; } return 0; }
注意数据会炸int,全部用ll。。。