HDU-6820 Tree (2020杭电多校(五) 1007)

HDU-6820 / 2020杭电多校(五) 1007 Tree

补题链接 参考博客

题意:

在一个无根树上选择一个的子图,要求子图全联通且度数大于k 的点最多只有1个。问该子图最大的权值。

题解:

  1. 首先想到枚举这个子图中度数大于k的点,然扩展一下。这个方法复杂度太高。

  2. 可以发现叶子节点很好处理,可以从下往上处理,想到树形DP。\(dp[u] [0]\) 表示以u为根的子图无大于k的点的最大权值。\(dp[u] [1]\) 表示以u为根的子图有一个大于k的点的最大权值。我们首先默认 u 必定和其父节点 pre 连边。那么在 u 在子节点中最多连 \(k - 1\) 个节点。(这样做的原因是为了能够向上dp,如果u已经连了k个子节点,处理pre时无法确定与哪些子节点能连边)。 由于是最大权重,所以为了处理方便我们可以先对子节点按照\(dp[v][0] + w\) 从大到小排序。

  3. \(dp[u][0]:​\)

\[ dp[u][0]=\sum_{i = 1}^{k-1}(dp[v_{i}][0]+w) \]

  1. $dp[u][1]: $ 更新\(dp[u][1]\) 情况较多,需要仔细思考。先设u为根的子图中度数大于k的点为p。

    • 情况1:\(p=u\) , 这时u是度大于k的点,那么为了权值更大肯定尽量多的连边。

      \[dp[u][1]=\sum_{i = 1}^{sn}(dp[v][0]+w) \]

    • 情况2:\(p=v_{0}​\) , 即度数大于k的点是u的一个子节点。这时u在子节点中连1个\(dp[v][1]​\)\(k-2​\)\(dp[v] [0]​\)。为了使权值最大选\(dp[v][0]​\) 时一定从最大的点开始选择。由于子节点排好序,所以我们只需要讨论一下\(v_{0}​\)是不是前\(k-1​\)个节点。设\(sum=\sum_{i=1}^{k-1}(dp[v_{i}][0]+w)​\)

      • 1)\(v_{0}\)是前k-1个节点,那么就需要多选择一个第k-1个节点。(其中可以用\(dp[u][0]\)代替sum)

        \[dp[u][1]=max\{sum - (dp[v][0]+w) + (dp[v][1]+w)\}\\ dp[u][1]=max\{dp[u][0]-dp[v][0]+dp[v][1]\} \]

      • 2)\(v_{0}\)不是前k-1个点,那么就只需要减去第k-1个点的值,加上\(v_{0}\) 的值就可以。其中mink1是第k-1个子节点的 \(dp[v][0]\)+ w。

        \[dp[u][1]=max\{dp[u][0]-mink1+dp[v][1]+w\} \\ \]

    三种情况计算的\(dp[u][1]\)取最大值。

  2. 前面部分dp时默认u与pre连边,现在我们要计算不u与pre不连边时的最大值。设\(maxx1\)\(maxx2\) 分别表示此时的 \(dp[u][0]\)\(dp[u][1]\)

    • 若子节点个数大于等于k,\(maxx1=dp[u][0]+mink\) 。 否则 \(maxx1=dp[u][0]\)

    • 更新maxx2 : 和计算\(dp[u][1]\) 的过程一样。不过\(dp[u][1]\) 是讨论\(v_{0}\) 是前K-1个点。\(maxx2\) 讨论 \(v_{0}\) 是前k个点。

      \[v属于{1...k})\ \ maxx2 = max\{maxx1-dp[v][0]+dp[v][1]\} \\ 其他\ \ maxx2=max(maxx1-mink+dp[v][1]+w) \]

  3. 最后答案res = max(maxx1, maxx2)

代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
#include<vector>
#include<string>
#include<fstream>
using namespace std;
#define rep(i, a, n) for(int i = a; i <= n; ++ i)
#define per(i, a, n) for(int i = n; i >= a; -- i)
typedef long long ll;
const int N = 2e6 + 105;
const int mod = 998244353;
const double Pi = acos(- 1.0);
const int INF = 0x3f3f3f3f;
const int G = 3, Gi = 332748118;
ll qpow(ll a, ll b) { ll res = 1; while(b){ if(b & 1) res = (res * a) % mod; a = (a * a) % mod; b >>= 1;} return res; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
//

int n, k;
int head[N], cnt = 0;
ll dp[N][2];
ll res;

struct node{
    int to, nxt; ll c;
}edge[N];

void add(int u, int v, ll w){
    edge[cnt].to = v, edge[cnt].c = w, edge[cnt].nxt = head[u], head[u] = cnt ++;
    edge[cnt].to = u, edge[cnt].c = w, edge[cnt].nxt = head[v], head[v] = cnt ++;
}

bool cmp(int a, int b){
    return dp[edge[a].to][0] + edge[a].c > dp[edge[b].to][0] + edge[b].c;
}

void dfs(int u, int pre){
    dp[u][0] = dp[u][1] = 0;
    vector<int> sol; sol.push_back(0);
    int sn = 0; //sonnum;
    for(int i = head[u]; i != -1; i = edge[i].nxt){
        int v = edge[i].to; ll w = edge[i].c;
        if(v == pre) continue;
        dfs(v, u);
        sol.push_back(i);
        sn ++;
    }
    sort(sol.begin() + 1, sol.begin() + 1 + sn, cmp);
    
    ll maxx1 = 0, maxx2 = 0;
    //dp[u][0] and maxx1
    for(int i = 1; i <= min(k - 1, sn); ++ i){
        int v = edge[sol[i]].to; ll w = edge[sol[i]].c;
        dp[u][0] += dp[v][0] + w;
    }
    if(sn >= k) maxx1 = dp[u][0] + dp[edge[sol[k]].to][0] + edge[sol[k]].c;
    else maxx1 = dp[u][0];
    res = max(res, maxx1);
    
    // dp[u][1] and maxx2
    for(int i = 1; i <= sn; ++ i){
        int v = edge[sol[i]].to; ll w = edge[sol[i]].c;
        dp[u][1] += dp[v][0] + w;
    }
    maxx2 = dp[u][1];
    
    for(int i = 1; i <= sn; ++ i){
        int v = edge[sol[i]].to; ll w = edge[sol[i]].c;
        if(i <= k - 1) dp[u][1] = max(dp[u][1], dp[u][0] - dp[v][0] + dp[v][1]);
        else if(k > 1){
            ll mink1 = dp[edge[sol[k - 1]].to][0] + edge[sol[k - 1]].c;
            dp[u][1] = max(dp[u][1], dp[u][0] - mink1 + dp[v][1] + w);
        }
    }
    
    for(int i = 1; i <= sn; ++ i){
        int v = edge[sol[i]].to; ll w = edge[sol[i]].c;
        if(i <= k) maxx2 = max(maxx2, maxx1 - dp[v][0] + dp[v][1]);
        else if(k > 0){
            ll mink = dp[edge[sol[k]].to][0] + edge[sol[k]].c;
            maxx2 = max(maxx2, maxx1 - mink + dp[v][1] + w);
        }
    }
    res = max(res, maxx2);
}

void init(){
    cnt = 0;
    res = 0;
    for(int i = 0; i <= n + 2; ++ i){
        head[i] = -1;
        dp[i][0] = dp[i][1] = 0;
    }
}

int main()
{
    int T; scanf("%d",&T);
    while(T --){
        scanf("%d%d",&n,&k);
        init();
        for(int i = 1; i < n; ++ i){
            int x, y; ll z; scanf("%d%d%lld",&x,&y,&z);
            add(x, y, z);
        }
        if(!k){
            printf("0\n");
            continue;
        }
        dfs(1, 0);
        printf("%lld\n", res);
    }
    return 0;
}
posted @ 2020-08-05 15:47  A_sc  阅读(184)  评论(1编辑  收藏  举报