ZOJ 3949 Edge to the Root(树形DP)

4 篇文章 0 订阅

题意

给定一棵树,选一个节点和根相连,使得最后所有点到根的距离和最小,求最小距离和

思路

首先一个点和根连起来以后,影响的只有根到这个点这条路径上的点,以及他们的子树,并且对于每个点影响的权值都是能够确定的。
一个点减少多少距离,那么子树中的点也会减少多少距离。

从路径的中点以下,因为连了根减少的权值为2-4-6…以此类推

虽然树形DP怎么打都行,但是不知道为什么网上找到的代码都特别复杂,看了半天才看懂。然后发现大部分代码都是没用的。。

因为权值改变是有规律的,所以我们可以用回溯来枚举连接点和抵消变化

  • 先做一遍搜索找出每个点的子树大小
  • 用sum[dep]的差值表示从父节点到当前节点时,会影响的父节点子树中除自己这支以外的所有点个数
  • 从中点枚举到当前点,中点子树中除自己这支以外的所有点个数应该减掉,因为相对父节点来说,距离增加了1
  • 从父节点枚举到当前点,应该再加上当前点子树的大小,因为相对父节点来说,距离减少了1

代码

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=a;i<b;i++)
#define sc(a) scanf("%d",&a)
const int INF=0x3f3f3f3f;
const int maxn=2e5+50;
const int mod=1e9+7;
const double eps=1e-8;
#define pii pair<int,int>
typedef long long ll;
typedef unsigned int ui;
using namespace std;

int n;
vector<int> G[maxn];
int cnt[maxn];
ll sum[maxn];

ll totDis;
ll gAns;

int dfs1(int fa,int cur,int depth){
    int &tot=cnt[cur]=1;
    for(int i=0;i<G[cur].size();i++){
        int v=G[cur][i];
        if(v!=fa)
            tot+=dfs1(cur,v,depth+1);
    }
    //printf("%d %lld %d\n",cur,totDis,tot);
    totDis+=depth;
    return tot;
}

void dfs2(int cur,int fa,int dep,ll ans){
    if(dep) sum[dep]=sum[dep-1]+cnt[fa]-cnt[cur];
    //printf("dep %d sum_dep %lld\n",dep,sum[dep]);
    int halfDep=(dep+1)/2;
    int s=halfDep+1, t=dep;
    if(dep>1){
        if(dep&1) s--;
        ans-=sum[t]-sum[s];
        ans+=cnt[cur];
    }
    //printf("s= %d t= %d u=%d depth=%d ans=%lld\n",s,t,cur, dep, ans);
    gAns=max(ans,gAns);
    for(int i=0;i<G[cur].size();i++){
        int v=G[cur][i];
        if(v!=fa)
            dfs2(v,cur,dep+1,ans);
    }
}

int main()
{
#ifndef ONLINE_JUDGE
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
#endif

    int T; scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        rep(i,1,n+1) {
            G[i].clear();
        }
        int u,v;
        rep(i,1,n) {
            sc(u); sc(v);
            G[u].push_back(v);
            G[v].push_back(u);
        }
        mem(cnt,0); gAns=0;
        totDis=0;
        dfs1(-1,1,0);
        mem(sum,0);
        dfs2(1,-1,0,0);
        printf("%lld\n",totDis-gAns);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值