HDU多校第三场1011 Squrirrel —— 换根 + 树形DP

mac2026-08-10  8

题目链接:点我啊╭(╯^╰)╮

题目大意:

    在一棵无根树上,有边权,可以将某一条边的边权置 0 0 0     问最坏情况下,从某个结点出发到最远结点的距离最小的是多少???

解题思路:

    设 d p [ r t ] [ 0 ] dp[rt][0] dp[rt][0] 为不让边权置 0 0 0,以 r t rt rt 为根到子树结点的最远距离     则 d p [ r t ] [ 1 ] dp[rt][1] dp[rt][1] 为让一条边权置 0 0 0,以 r t rt rt 为根到子树结点的最远距离      s e [ r t ] [ 0 ] se[rt][0] se[rt][0] 为第二远,表示相同

     d p [ r t ] [ 0 ] = m a x ( d p [ r t ] [ 0 ] , d p [ s o n ] [ 0 ] + w ) dp[rt][0] = max(dp[rt][0], dp[son][0] + w) dp[rt][0]=max(dp[rt][0],dp[son][0]+w)     若 s o n son son d p [ r t ] [ 0 ] dp[rt][0] dp[rt][0] 的决策点,则 d p [ r t ] [ 1 ] dp[rt][1] dp[rt][1] 肯定与 s o n son son 相关:      d p [ r t ] [ 1 ] = m a x ( s e [ r t ] [ 0 ] , m i n ( d p [ s o n ] [ 0 ] , d p [ s o n ] [ 1 ] + w ) ) dp[rt][1] = max(se[rt][0], min(dp[son][0], dp[son][1]+w)) dp[rt][1]=max(se[rt][0],min(dp[son][0],dp[son][1]+w))

    要求最坏情况下距离最小,只需要保证不置 0 0 0的距离最大的同时,置 0 0 0的距离距离尽量小


    考虑换根,因为每个节点只保存了到子树的信息     所以从一个根换到另一个根,只需要改变这两个结点的信息即可

    结构体 n o d e node node 表示以 i d id id 为为儿子, d p 0 dp0 dp0为不置 0 0 0 d p 1 dp1 dp1为置 0 0 0的信息     利用 m a p < n o d e , i n t > m [ m a x n ] map <node, int> m[maxn] map<node,int>m[maxn] 排好序     从 r t rt rt 换到 s o n son son,将 m [ r t ] m[rt] m[rt] 指向 s o n son son 儿子的信息删掉     同时 m [ s o n ] m[son] m[son] 要加上指向 r t rt rt 为儿子的信息

    注意这里需要删掉信息,同时需要维护前两远的信息,因此需要维护前三远的信息


    由于换根利用到了 m a p map map,常数比较大,可以用 v e c t o r vector vector 手动 s o r t sort sort     或者直接用数组手动比较,但 m a p map map 写的确实方便     下面总结了针对的卡常技巧:     ①:inline     ②:控制 m a p map map 大小:while(m[rt].size()>3) m[rt].erase(--m[rt].end());     ③:采用 爆炸输入输出挂~~~     ④:多交几十发     ⑤:改vector


    本题必须采用 ① 和 ②,不加爆炸输入挂的话有百分之十的几率可以卡到 3900 m s 3900ms 3900ms     加了爆炸输入挂稳定在 3000 m s 3000ms 3000ms     考虑这题是多组并且是在 H D U HDU HDU 上,说明常数的控制还是比较客观的     实在不行在换 v e c t o r vector vector


    本题提供了一种模板,可以说是一种惯用写法     会陆续更新一些套用本模板的换根 D P DP DP     这类题都可以用数组的形式去写,只不过要多几十行代码      P S : PS: PS我不是这套模板的原创作者

核心:利用子树信息换根

这里的代码没有加入 爆炸输入输出挂~~~

#include<bits/stdc++.h> #define fi first #define se second using namespace std; typedef long long ll; using pii = pair <int,int>; const int maxn = 2e5 + 5; int T, n, dp[maxn][2], ans, pos; vector <pii> g[maxn]; struct node{ int dp0, dp1, id; bool operator < (const node &A)const { if(dp0 ^ A.dp0) return dp0 > A.dp0; if(dp1 ^ A.dp1) return dp1 < A.dp1; return id < A.id; } }; map <node, int> m[maxn]; map <node, int>::iterator it; inline void get(int rt){ if(m[rt].size() == 0){ dp[rt][0] = dp[rt][1] = 0; return; } it = m[rt].begin(); int dp1 = it->fi.dp1; dp[rt][0] = it->fi.dp0; dp[rt][1] = max(dp1, (++it)->fi.dp0); while(m[rt].size()>3) m[rt].erase(--m[rt].end()); } inline void dfs1(int u, int fa){ for(auto v : g[u]){ if(v.fi == fa) continue; dfs1(v.fi, u); int dp0 = dp[v.fi][0] + v.se; int dp1 = min(dp[v.fi][0], dp[v.fi][1] + v.se); m[u][ {dp0, dp1, v.fi} ]++; } get(u); } inline void move(int rt, int son, int w){ m[rt].erase( {dp[son][0] + w, min(dp[son][0], dp[son][1] + w), son} ); get(rt); m[son][ {dp[rt][0] + w, min(dp[rt][0], dp[rt][1] + w), rt} ]++; get(son); } inline void dfs2(int u, int fa){ if(ans > dp[u][1]){ ans = dp[u][1]; pos = u; } for(auto v : g[u]){ if(v.fi == fa) continue; move(u, v.fi, v.se); dfs2(v.fi, u); move(v.fi, u, v.se); } } int main() { scanf("%d", &T); while(T--){ ans = 2e9; scanf("%d", &n); for(int i=1; i<=n; i++) g[i].clear(), m[i].clear(); for(int i=1, u, v, w; i<n; i++){ scanf("%d%d%d", &u, &v, &w), g[u].push_back({v, w}); g[v].push_back({u, w}); } dfs1(1, 0); dfs2(1, 0); printf("%d %d\n", pos, ans); } }
最新回复(0)