ZOJ 3949 Edge to the Root 树形DP

ZOJ3949

题意很简单,给定一个树,节点1e+5 问增加一条根节点到任意点的边,使得所有点到根节点的距离和最小,求这个最小距离和。

当我们连接(1,x)时,距离会减小的点显然是1->x链上在中点之后子树。

怎样高效计算呢? 其实我们可以在dfs的同时维护根节点到当前节点x这条链的信息, 用当前节点的S减去中点的S 就可以求得使得距离和减小的值,一个dfs后就可以找到最优解。

代码很简单

#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
typedef long long LL;
const int N=200010;
vector<int> G[N];
int cnt,st[N],size[N],d[N];
LL C[N],S[N],ans,all;
void dfs(int x,int fx){
    d[x]=d[fx]+1;
    all+=d[x];
    size[x]=1;
    for(int i=0;i<G[x].size();i++){
        int v=G[x][i];
        if(v!=fx){
            dfs(v,x);
            size[x]+=size[v];
        }
    }
}
void dfs2(int x,int fx){
    if(cnt)S[cnt]-=C[cnt]*size[x];
    st[++cnt]=x;
    C[cnt]=1-2*d[x];
    S[cnt]=S[cnt-1]+C[cnt]*size[x];
    if(fx){
        int mid=(cnt+1)/2;
        ans=min(ans,S[cnt]-S[mid]+1LL*size[st[mid+1]]*d[x]);
    }
    for(int i=0;i<G[x].size();i++){
        int v=G[x][i];
        if(v!=fx)dfs2(v,x);
    }if(--cnt)S[cnt]+=C[cnt]*size[x];
}
int T,n;
int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)G[i].clear();
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
        }d[all=ans=0]=-1;
        dfs(1,0);
        dfs2(1,0);
        printf("%lld\n",all+ans);
    }return 0;
}

  

时间: 2024-12-20 07:09:44

ZOJ 3949 Edge to the Root 树形DP的相关文章

ZOJ 3949 Edge to the Root(树形DP)

[题目链接] http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3949 [题目大意] 给出一棵根为1的树,每条边边长为1,请你从根连一条边到某个点, 使得各点到根距离的总和最小,求这个最小距离和 [题解] 假设从1连到x,那么受影响的只有1和x中点往下的部分, 我们发现中点往下的部分根据每个点答案改变的大小可以分为多个部分, 每个部分大小为其1-x链上的父节点子树大小减去其链上第一子节点子树大小, 设其链上父节点为y,那么

ZOJ 3949 Edge to the Root

题意: 在一棵树中,可以从根节点往其他节点加一条边,使得根节点到其他所有节点的距离和最小,输出最小的距离和. 思路: 我们考虑在加的一条边为$1 \to v$,那么在树上从$1 \to v$的路径上,如果有一个点$y$到$v$比到$1$更近,那么这个点$y$的子树里的所有 点都到$v$更近.那么我们找到离根最近的点$y$,那么$y$子树中的所有点都是到$v$更近. 我们考虑: $f[u]$表示如果添加了$1 \to u$这条边的最小距离和是多少. $g[u]$表示如果添加了$1 \to u$这条

zoj 3626 Treasure Hunt I (树形dp)

题目大意: 给出一棵树,求出从起点开始走m长度最后回到起点,所能得到的宝藏的最大价值. 思路分析: 通过一次dfs可以得到的是子树到根节点的所有距离的最大值. 现在的问题就是他走完一颗子树可以去另外一颗子树. 所以在回溯到根的时候要统计其他子树上互补距离的最大值. dp[i] [j] 表示i为根节点,在i的子树中走j步然后回到i所能拿到的最大价值. 转移方程就是 dp[x][i+2*len]=max(dp[x][i+2*len],dp[v][j]+dp[x][i-j]); v为x的子树的根,le

ZOJ 3626 Treasure Hunt I(树形dp)

Treasure Hunt I Time Limit: 2 Seconds      Memory Limit: 65536 KB Akiba is a dangerous country since a bloodsucker living there. Sometimes the bloodsucker will appear and kill everyone who isn't at his hometown. One day, a brave person named CC finds

ZOJ 3201 树形dp+背包(简单题)

#include<cstdio> #include<vector> #include<cstring> #include<iostream> using namespace std; const int MAXN = 110; vector<int>a[MAXN]; int n,m,v[MAXN],vis[MAXN],dp[MAXN][MAXN]; void dfs(int root) { dp[root][1] = v[root]; vis[r

poj 2342(树形DP)

Anniversary party Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 6062   Accepted: 3490 Description There is going to be a party to celebrate the 80-th Anniversary of the Ural State University. The University has a hierarchical structure

BZOJ 2878: [Noi2012]迷失游乐园( 树形dp )

一棵树的话直接树形dp(求出往下走和往上走的期望长度). 假如是环套树, 环上的每棵树自己做一遍树形dp, 然后暴力枚举(环上的点<=20)环上每个点跑经过环上的路径就OK了. --------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm&

POJ 1155 TELE 背包型树形DP 经典题

由电视台,中转站,和用户的电视组成的体系刚好是一棵树 n个节点,编号分别为1~n,1是电视台中心,2~n-m是中转站,n-m+1~n是用户,1为root 现在节点1准备转播一场比赛,已知从一个节点传送数据到达另一个节点,电视台需要一定的费用 若可以传送数据到达用户的节点n-m+1~n,这些用户各自愿意支付一定的费用给电视台 现在电视台希望在不亏本的情况下为尽量多的用户转播比赛 输出最多可以为多少用户转播比赛 背包类型的树形DP第一题 dp[i][j]表示以节点i为根的子树有j个用户获得转播,电视

poj 1947 经典树形dp

经典树形dp:问在一棵树上最少删除多少条边可以分离出一个节点数为p的子树. 定义状态: dp[i][j]表示从i为根的子树上分离出一个节点数为j的子树的代价(最少需要删除的边数). 考虑i节点的每个儿子ii,ii可以选或者不选(切断),然后就转化成了背包问题. dp[u][j] = min( dp[u][j], dp[u][j - k] + dp[v][k] ); 1 #include <iostream> 2 #include <cstring> 3 #include <c