CF1060 E-Sergey and Subway

题目戳这里

一句话题意

一棵树,任意相隔一个点的两个点连一条新边(原边留下),问所有点对的距离之和。

Solution

本来看见是黑题有点怕,但仔细一想也没有那么难。
先处理出每个点的深度(dep)和子树大小(size),我们把原树上的边称为老边,新建的边是新边。
首先因为隔了一个点有一条新边,假设新边跨过的点为 u ,那么这条新边产生的价值就是size[i]*(n-size[i])/2,也就是两边点对数量。那么老边会不会产生价值呢?如果原树上两点之间的距离为奇数,那么新图上肯定需要经过一条老边。所以答案还需要加上 (深度为奇的点数×深度为偶的点数)/2。其实还是比较好写的。

coding

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
struct Tree
{
    int to,next;
}e[N*2];
int n,head[N],cnt,dep[N],size[N];
long long ans,num;
void add(int x,int y)
{
    e[++cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt;
}
void DFS(int x,int last)
{
    size[x]=1;
    for(int i=head[x];i;i=e[i].next)
    {
        int v=e[i].to;
        if(v==last) continue ;
        dep[v]=dep[x]+1;
        DFS(v,x);
        size[x]+=size[v];
    }
}
int main()
{
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y),add(y,x);
    }
    DFS(1,0);
    for(int i=1;i<=n;i++)
    {
        ans+=size[i]*(long long)(n-size[i]);
        if(dep[i]%2==1) num++;
    }
    ans+=num*(long long)(n-num);
    cout<<ans/2;
    return 0;
}
posted @ 2018-10-05 19:34  Le_Mon  阅读(363)  评论(0编辑  收藏  举报