ZOJ - 3949 Edge to the Root

Edge to the Root

题意是给出一个棵树,每条边权都为1,要求在点1和点x之间连一条边,使得连边之后,点1到所有点的路径长度之和最小。
输出最小长度之和。
显然对于一棵树,添一条边之后,形成了一个环,一个在1-x的路径上的环,这个环的形成只对环上的点以及他们的子树上的点的距离产生了影响。 更准确地说,是对环的中点一下的点及子树产生了影响。
cnt数组记录每个点的子树大小+1(加上自己)。
假设环上的点存在cycle数组中,下标从0开始,中点下标为half,共有tot个点,
如果环上点个数为奇数,
那么添边后的收益
=2*(cnt[ cycle[half+1] - cycle[half+2] ])+4*(cnt[ cycle[half+2] - cycle[half+3] ])+6*(…)+2*(tot-half)*cnt[ cycle[tot] ];
没错,最后一个是cnt[ cycle[tot] ],不是cnt[ cycle[tot-1]-cycle[tot] ]。
如果环上点个数为偶数,
那么添边后的收益
=1*(cnt[ cycle[half+1] - cycle[half+2] ])+3*(cnt[half+2] - cnt[ cycle[half+3] ])+…+(2*(tot-half)-1)*cnt[tot]。

cnt数组可以由一次dfs得到,处理收益的过程可以由另一次dfs完成,因为计算收益的公式是有规律的,不同点的收益可以相互转化。

#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<list>
#include<numeric>
using namespace std;
#define PI acos(-1.0)
#define LL long long
#define ULL unsigned long long
#define LS (root<<1)
#define RS ((root<<1)|1)
#define LSON LS,l,mid
#define RSON RS,(mid+1),r
#define MID mid=((l+r)/2)
#define PR pair<int,int>
#define se second
#define MP make_pair
#define INF 0x3f3f3f3f
#define mm(a,b) memset(a,b,sizeof(a))
#define PP puts("*********************");
#define MOD 1000000007
#define idx(x) x-'a'
template<class T> T f_max(T a, T b){ return a > b ? a : b; }
template<class T> T f_min(T a, T b){ return a < b ? a : b; }
template<class T> T f_abs(T a){ return a > 0 ? a : -a; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
template<class T> void f_swap(T &a, T &b){ T c; c = a; a = b; b = c; }
// 0x3f3f3f3f3f3f3f3f
// 0x3f3f3f3f
const int maxn=2e5+5;
vector<int> G[maxn];
int cnt[maxn], dep[maxn];
int path[maxn];
LL sum[maxn];
LL all;
LL f_ans=0;
void Init(int n){
    for(int i=1;i<=n;i++)
        G[i].clear();
    all=0; f_ans=0;
}
void Dfs(int u,int fa,int depth){
    cnt[u]=1; dep[u]=depth;
    all+=dep[u];
    for(int i=0, siz=G[u].size();i<siz;i++){
        int v=G[u][i];
        if(v==fa) continue;
        Dfs(v, u, depth+1);
        cnt[u]+=cnt[v];
    }
}
void Dfs2(int u,int fa,int depth,LL ans){
    path[depth]=u;
    if(depth) sum[depth]=sum[depth-1]+cnt[fa]-cnt[u];
    int half_depth=(depth+1)/2;
    if(depth>1){
        if(dep[u]%2==0){
            int s=half_depth+1, t=depth;
            ans-=1LL*2*(t-s)*cnt[u];
            ans-=(sum[t]-sum[s]);
            ans+=1LL*(2*(t-s)+1)*cnt[u];
        }else{
            int s=half_depth+1, t=depth;
            ans-=1LL*(2*(t-s)+1)*cnt[u];
            ans-=(sum[t]-sum[s-1]);
            ans+=1L*2*(t-s+1)*cnt[u];
        }
    }
//    printf("u=%d depth=%d ans=%d\n",u, depth, ans);
    f_ans=max(f_ans, ans);
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(v==fa) continue;
        Dfs2(v, u, depth+1, ans);
    }
}
int main(){
    int cas=0;
    int T;
    int n;
    int u, v;

    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        Init(n);
        for(int i=1;i<n;i++){
            scanf("%d%d",&u, &v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        Dfs(1,0,0);
        sum[0]=0;
        Dfs2(1,0,0,0);
        printf("%lld\n",all-f_ans);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值