首页 > 代码库 > 蒜头君的树

蒜头君的树

蒜头君的树

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

输入格式

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

接下来 n-1n?1 行,每行两个正整数 x_i,y_ix?i??,y?i??,x_ix?i?? 表示 i+1i+1 号结点的父亲结点的编号,保证其父结点编号小于自己编号。y_iy?i?? 表示 i+1i+1 号结点的父亲结点与自己间边的距离。

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

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

输出格式

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

接下来 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;
}

 

 

 

蒜头君的树