Valley tree DP (Foundation)

Keywords: ICPC

First 22 questions of Luogu tree DP (basic)

xzy's tree dp problem list - problem list - Luogu | new ecology of Computer Science Education (luogu.com.cn)

P1122 largest subtree and - Luogu | new ecology of Computer Science Education (luogu.com.cn)

Topic meaning: delete the edge of a tree and judge the sum of the maximum weights of connected blocks after deleting the edge?

definition f [ c u r ] f[cur] f[cur] c u r cur cur is the maximum sum of the weights of the subtree of the root

Initial value f [ c u r ] = w [ c u r ] f[cur] = w[cur] f[cur]=w[cur]

If one of his subtrees is greater than 0, select this subtree. f[cur] += max(f[p], 0);

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 16100;
int n, id;
int w[MAXN], head[MAXN], f[MAXN], ans = -2147483647;

struct Edge{
    int to, next;
}E[MAXN * 2];

void addedge(int u, int v)
{
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
    return ;
}

void dfs(int cur, int father)
{
    for(int i = head[cur]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == father) continue;
        dfs(p, cur);
        f[cur] += max(f[p], 0);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d", &w[i]);
        f[i] = w[i];
    }
    for(int i = 1; i < n; i++)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        addedge(u, v);
        addedge(v, u);
    }
    dfs(1, -1);
    int ans = -2147483647;
    for(int i = 1; i <= n; i++) ans = max(ans, f[i]);
    printf("%d\n", ans);
    return 0;
}

P1352 dance without boss - New Ecology of computer science education in Luogu (luogu.com.cn)

Introduction to tree DP

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 6e3 + 60;
int n, id;
int w[MAXN], head[MAXN], cnt[MAXN], f[MAXN][2];
struct Edge{
    int to, next;
}E[MAXN * 2];

void addedge(int u, int v)
{
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
    return;
}

void dfs(int cur, int father)
{
    f[cur][0] = 0;
    f[cur][1] = w[cur];
    for(int i = head[cur]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == father) continue;
        dfs(p, cur);
        f[cur][0] += max(f[p][0], f[p][1]);
        f[cur][1] += f[p][0];
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
    for(int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        addedge(u, v);
        addedge(v, u);
        cnt[u]++;
        cnt[v]++;
    }

    int root = -1;

    for(int i = 1; i <= n; i++)
    {
        if(cnt[i] == 1) {
            root = i;
            break;
        }
    }

    dfs(root, -1);

    int ans = max(f[root][0], f[root][1]);
    printf("%d\n", ans);

    return 0;
}

P2015 binary apple tree - Luogu | new ecology of Computer Science Education (luogu.com.cn)

Tree Backpack

f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] denotes by i i i is a subtree, j j j for consideration j j j nodes have been selected k k The maximum value of k edges. (p is its child node)

f [ u ] [ j ] [ k ] = m a x ( f [ u ] [ j − 1 ] [ x ] + f [ p ] [ p son junction spot of number amount ] [ k − x − 1 ] + w ) f[u][j][k]=max(f[u][j-1][x]+f[p][p number of child nodes] [k-x-1]+w) f[u][j][k]=max(f[u][j − 1][x]+f[p][p number of child nodes] [K − x − 1]+w)

We can consider optimizing the middle dimension

f [ i ] [ j ] f[i][j] f[i][j] denotes by i i i is the node of the subtree j j j edge.

Consider the nodes on a tree u u u and its child nodes.

d p [ u ] [ j ] = m a x ( d p [ u ] [ k ] + d p [ p ] [ j − k − 1 ] + w ) dp[u][j] = max(dp[u][k] + dp[p][j - k - 1]+w) dp[u][j]=max(dp[u][k]+dp[p][j−k−1]+w)

This is an obvious tree backpack.

Note that when enumerating the backpack capacity, you need to enumerate in reverse order (equivalent to the optimization of 01 backpack) (enumerate the backpack capacity first, and then enumerate the decision-making)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 105;
int n, q, id, ans;
int head[MAXN], f[MAXN][MAXN];
struct Edge{
    int to, next, w;
}E[MAXN * 2];

void addedge(int u, int v, int w)
{
    E[id].to = v;
    E[id].w = w;
    E[id].next = head[u];
    head[u] = id++;
    return;
}

void dfs(int u, int fa)
{
    f[u][0] = 0;
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to, w = E[i].w;
        if(p == fa) continue;
        dfs(p, u);
        for(int i = q; i >= 1; i--)
        {
            for(int j = 0; j < i; j++)
            {
                f[u][i] = max(f[u][i], f[u][j] + f[p][i - j - 1] + w);
            }
        }
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &q);
    for(int i = 1; i < n; i++)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        addedge(u, v, w);
        addedge(v, u, w);
    }
    dfs(1, -1);
    ans = f[1][q];
    /*
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= q; j++)
            printf("%d ", f[i][j]);
        printf("\n");
    }*/
    printf("%d\n", ans);
    return 0;
}

[P2014 CTSC1997] course selection - New Ecology of computer science education in Luogu (luogu.com.cn)

Ibid

Topic meaning: course selection is a dependent tree knapsack. Only the parent node is selected can the child node be selected.

Different from the previous question, there will be multiple trees in this question

Solution: add a node No. 0. The weight of node No. 0 is 0, which must be selected. Is the parent of all root nodes. The problem of multiple trees can be transformed into one tree.

The final answer is to choose m courses, that is f [ 0 ] [ m + 1 ] f[0][m+1] Value of f[0][m+1]

f [ u ] [ k ] = m a x ( f [ u ] [ j ] + f [ p ] [ k − j ] ) f[u][k] = max(f[u][j]+f[p][k-j]) f[u][k]=max(f[u][j]+f[p][k−j])

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 305;
int n, m, id;
int k[MAXN], head[MAXN], f[MAXN][MAXN];
struct Edge{
    int to, next;
}E[MAXN * 2];

void addedge(int u, int v)
{
    E[id].next = head[u];
    E[id].to = v;
    head[u] = id++;
    return;
}

void dfs(int u, int fa)
{
    f[u][1] = k[u];
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == fa) continue;
        dfs(p, u);
        for(int i = m + 1; i >= 1; i--)
        {
            for(int j = 1; j <= i; j++)
            {
                f[u][i] = max(f[u][i], f[u][j] + f[p][i - j]);
            }
        }
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &m);
    k[0] = 0;
    for(int i = 1; i <= n; i++)
    {
        int s;
        scanf("%d%d", &s, &k[i]);
        addedge(i, s);
        addedge(s, i);
    }

    dfs(0, -1);
    /*
    for(int i = 0; i <= n; i++)
    {
        for(int j = 0; j <= m + 1; j++)
        {
            printf("%d ", f[i][j]);
        }
        printf("\n");
    }
    */

    printf("%d\n", f[0][m + 1]);

    return 0;
}

P2016 strategy game - Luogu | new ecology of Computer Science Education (luogu.com.cn)

Minimum point coverage on tree

f[u][0] += f[p][1];
f[u][1] += min(f[p][0], f[p][1]);

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 1590;
int n, id;
int head[MAXN], f[MAXN][2];
struct Edge{
    int to, next;
}E[MAXN * 2];

void addedge(int u, int v)
{
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
}

void dfs(int u, int fa)
{
    f[u][0] = 0;
    f[u][1] = 1;
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == fa) continue;
        dfs(p, u);
        f[u][0] += f[p][1];
        f[u][1] += min(f[p][0], f[p][1]);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        int x, num;
        cin >> x >> num;
        for(int j = 1; j <= num; j++) {
            int u;
            cin >> u;
            addedge(u, x);
            addedge(x, u);
        }
    }
    dfs(1, -1);
    printf("%d\n", min(f[1][0], f[1][1]));
    return 0;
}

[P3478 Poi2008] sta Station - New Ecology of computer science education in Luogu (luogu.com.cn) (change DP)

Given a tree, find the sum of the depths of all nodes with a point in the tree as the root node

Change the root DP idea: the first time dfs can find the information of the root node, and the second time dfs can deduce the information of the child node according to the information of the parent node.

For the first time, dfs can calculate the sum of the depth values of all points of the root node (the depth is calculated in the order down) (the number of child nodes with the current point as the root is in the inverse dfs order)

Consider this: if you take the child node of the current root node as the root, the depth of the point (including itself) with the current child node as the root is - 1, and the depth of other points is + 1

This order is changed from top to bottom

f [ p ] = f [ u ] − s z [ p ] + n − s z [ p ] f[p]=f[u]-sz[p]+n-sz[p] f[p]=f[u]−sz[p]+n−sz[p]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 1e6 + 60;
int n, id;
ll head[MAXN], f[MAXN], depth[MAXN], sz[MAXN];
struct Edge{
    int to, next;
}E[MAXN * 2];

void addedge(int u, int v)
{
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
    return;
}

void dfs1(int u, int fa)
{
    sz[u] = 1;
    depth[u] = depth[fa] + 1;
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == fa) continue;
        dfs1(p, u);
        sz[u] += sz[p];
    }
}

void dfs2(int u, int fa)
{
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == fa) continue;
        f[p] = f[u] - sz[p] + (n - sz[p]);
        dfs2(p, u);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    cin >> n;
    for(int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        addedge(u, v);
        addedge(v, u);
    }
    dfs1(1, 0);
    for(int i = 1; i <= n; i++) f[1] += depth[i];
    dfs2(1, 0);
    ll ans = 0;
    int res;
    for(int i = 1; i <= n; i++) {
        if(ans < f[i]) {
            ans = f[i];
            res = i;
        }
    }

    printf("%d\n", res);

    return 0;
}

[P1040 NOIP2003 improvement group] bonus binary tree - Luogu | new ecology of Computer Science Education (luogu.com.cn)

Interval DP

For a sequence, the number on its left constitutes its left subtree, and the number on its right constitutes its right subtree.

f [ l ] [ r ] = f [ l ] [ k − 1 ] ∗ f [ k + 1 ] [ r ] + f [ k ] [ k ] f[l][r]=f[l][k-1]*f[k+1][r]+f[k][k] f[l][r]=f[l][k−1]∗f[k+1][r]+f[k][k]

Note that the left subtree and the right subtree are empty

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 32;
int n;
ll w[MAXN], f[MAXN][MAXN], root[MAXN][MAXN];

void print(int l, int r)
{
    if(l > r) return;
    int ro = root[l][r];
    printf("%lld ", ro);
    print(l, ro - 1);
    print(ro + 1, r);
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++) {
        scanf("%lld", &w[i]);
        f[i][i] = w[i];
        root[i][i] = i;
    }
    for(int len = 2; len <= n; len ++)
    {
        for(int l = 1; l + len - 1 <= n; l ++)
        {
            int r = l + len - 1;
            for(int k = l; k <= r; k ++)
            {
                if(k == l) {
                    if(f[l][r] < f[l + 1][r] + f[l][l])
                    {
                        f[l][r] = f[l + 1][r] + f[l][l];
                        root[l][r] = l;
                    }
                }
                else if(k == r) {
                    if(f[l][r] < f[l][r - 1] + f[r][r])
                    {
                        f[l][r] = max(f[l][r], f[l][r - 1] + f[r][r]);
                        root[l][r] = r;
                    }
                }
                else {
                    if(f[l][r] < f[l][k - 1] * f[k + 1][r] + f[k][k])
                    {
                        f[l][r] = f[l][k - 1] * f[k + 1][r] + f[k][k];
                        root[l][r] = k;
                    }
                }
            }
        }
    }
    printf("%lld\n", f[1][n]);
    print(1, n);
    cout << endl;
    return 0;
}

Cf219d choosing capital for tree and - New Ecology of computer science education in Luogu (luogu.com.cn) (change DP)

Given a tree, all edges are unidirectional. The capital is defined as a path to other points. Ask which point is selected as the capital to minimize the number of flipped edges.

When saving a graph, two-way edges are saved. The existing edge is set to 1 and the non-existent edge is set to 0.

In the first dfs, calculate the number of reversed edges required by node 1 from bottom to top.

During the second dfs, judge the direction of the edge between the current child node and the parent node from top to bottom to determine the direction f [ p ] = f [ u ] − 1 or yes f [ u ] + 1 f[p] = f[u]-1 or f[u]+1 f[p]=f[u] − 1 or f[u]+1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 2e5 + 50;
int n, id;
int head[MAXN], f[MAXN];
struct Edge{
    int to, next, w;
}E[MAXN * 2];

void addedge(int u, int v, int w)
{
    E[id].to = v;
    E[id].w = w;
    E[id].next = head[u];
    head[u] = id++;
    return;
}

void dfs1(int u, int fa)
{
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to, w = E[i].w;
        if(p == fa) continue;
        dfs1(p, u);
        f[u] += (f[p] + w);
    }
}

void dfs2(int u, int fa)
{
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to, w = E[i].w;
        if(p == fa) continue;
        if(w == 0) f[p] = f[u] + 1;
        else f[p] = f[u] - 1;
        dfs2(p, u);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d", &n);
    for(int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        addedge(u, v, 0);
        addedge(v, u, 1);
    }
    dfs1(1, -1);
    // printf("%d\n", f[1]);
    dfs2(1, -1);
    int minn = f[1];
    for(int i = 1; i <= n; i++) minn = min(minn, f[i]);
    printf("%d\n", minn);
    for(int i = 1; i <= n; i++) {
        if(minn == f[i]) printf("%d ", i);
    }
    printf("\n");

    return 0;
}

P1270 "visit" Art Museum - New Ecology of computer science education in Luogu (luogu.com.cn)

Topic meaning: a gallery starts from the hall and takes a certain time to pass through each side. There are paintings in the gallery. It takes 5s to take a painting. Ask how many paintings can be taken away within the specified time.

Problems with tree backpacks:

The array should be large enough.

The thief finally needs to go out through the gate, so the time to go through each road is t i m e ∗ 2 time*2 time∗2

Can't just go out within the specified time, specified time - 1.

The difficulty lies in drawing:

The method of recursive drawing is adopted

If the current node is connected to 0 when there are drawings, you only need to f [ 0 ] [ 0 ] = 1 f[0][0] = 1 f[0][0]=1

Tree Backpack f [ u ] [ j ] f[u][j] f[u][j] means from to u u u nodes, spent j j j seconds, the maximum number of stolen paintings

It is also an enumeration in reverse order

f [ u ] [ j ] = m a x ( f [ u ] [ j − k − w ] + f [ p ] [ k ] ) f[u][j] = max(f[u][j-k-w]+f[p][k]) f[u][j]=max(f[u][j−k−w]+f[p][k])

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int M = 105, N = 2010;
int s, id, tot, root;
int head[N], f[N][N];

struct Edge{
    int to, next, w;
}E[M * 2];

void addedge(int u, int v, int w)
{
    E[id].to = v;
    E[id].w = w;
    E[id].next = head[u];
    head[u] = id++;
    return;
}

void input(int u, int fa)
{
    int time, num;
    scanf("%d%d", &time, &num);
    addedge(fa, u, time * 2);
    if(!num)
    {
        input(++tot, u);
        input(++tot, u);
    }
    else {
        while(num)
        {
            addedge(u, 0, 5);
            num--;
        }
    }
}

void dfs(int u, int fa)
{
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to, w= E[i].w;
        if(p == fa) continue;
        dfs(p, u);
        for(int j = s; j >= w; j --)
        {
            for(int k = 0; k <= j - w; k++)
            {
                f[u][j] = max(f[u][j], f[u][j - k - w] + f[p][k]);
            }
        }
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    cin >> s;
    s -- ;

    root = tot = 1;
    input(++tot, root);

    f[0][0] = 1;

    dfs(root, -1);
    printf("%d\n", f[root][s]);

    return 0;
}

[P1131 ZJOI2007] temporal synchronization - New Ecology of computer science education in Luogu (luogu.com.cn)

When adjusting the subtree, adjusting the parent node can use fewer props than adjusting the child node

So for each node u u u only needs to judge the distance from its farthest child node, and finally update it from bottom to top.

Remember to drive long long

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
typedef long long ll;
const int MAXN = 5e5 + 50;
ll head[MAXN], f[MAXN];
ll n, root, id, ans;

struct Edge{
    int to, next, w;
}E[MAXN * 2];

void addedge(int u, int v, int w)
{
    E[id].w = w;
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
    return;
}

void dfs(int u, int fa)
{
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to, w = E[i].w;
        if(p == fa) continue;
        dfs(p, u);
        f[u] = max(f[u], f[p] + w);
    }
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to, w = E[i].w;
        if(p == fa) continue;
        ans += f[u] - (f[p] + w);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &root);
    for(int i = 1; i < n; i++)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        addedge(u, v, w);
        addedge(v, u, w);
    }
    dfs(root, -1);
    cout << ans << endl;
    return 0;
}

[P2279 HNOI2003] establishment of Fire Department - New Ecology of computer science education in Luogu (luogu.com.cn)

One point can cover two points at a distance of two. Ask how many points on a tree can cover the whole tree

Greedy from bottom to top, covering the grandfather nodes of the nodes not covered each time (maintained with a priority queue)

For the first time, the depth is calculated by dfs, and for the second time, the coverage is simulated by dfs.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;
typedef pair<int, int> PII;
const int MAXN = 1010;
int n, id;
int head[MAXN], f[MAXN], d[MAXN];
bool vis[MAXN];
struct Edge{
    int to, next;
}E[MAXN * 2];
priority_queue<PII>pq;

void addedge(int u, int v)
{
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
}

void dfs1(int u, int fa)
{
    d[u] = d[fa] + 1;
    pq.push(make_pair(d[u], u));
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == fa) continue;
        dfs1(p, u);
    }
}

void dfs2(int u, int d)
{
    if(d > 2) return;
    vis[u] = true;
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        dfs2(p, d + 1);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d", &n);
    for(int i = 2; i <= n; i++)
    {
        scanf("%d", &f[i]);
        addedge(i, f[i]);
        addedge(f[i], i);
    }
    dfs1(1, -1);
    memset(vis, false, sizeof(vis));
    int ans = 0;
    while(!pq.empty())
    {
        while(!pq.empty() && vis[pq.top().second]) pq.pop();
        if(pq.empty()) break;
        auto u = pq.top();
        int d = u.first, id = u.second;
        dfs2(f[f[id]], 0);
        pq.pop();
        ans++;
    }

    printf("%d\n", ans);
    return 0;
}

There seems to be an idea of tree DP state machine

P1273 cable TV Network - New Ecology of computer science education in Luogu (luogu.com.cn) (tree backpack)

d p [ u ] [ i ] [ j ] dp[u][i][j] dp[u][i][j] stands for the second u u u nodes, considering its front i i i have a son, Unicom j j The greatest benefit of j residents.

d p [ u ] [ i ] [ j ] = m a x ( d p [ u ] [ i ] [ j ] , d p [ u ] [ i − 1 ] [ j − k ] + d p [ v ] [ f u l l s o n [ v ] ] [ k ] − w ) dp[u][i][j] = max(dp[u][i][j], dp[u][i-1][j-k] + dp[v][fullson[v]][k]-w) dp[u][i][j]=max(dp[u][i][j],dp[u][i−1][j−k]+dp[v][fullson[v]][k]−w)

Knapsack enumeration in reverse order is optimized into two dimensions

d p [ u ] [ j ] = m a x ( d p [ u ] [ j ] , d p [ u ] [ j − k ] + d p [ v ] [ k ] − w ) dp[u][j] = max(dp[u][j], dp[u][j-k]+dp[v][k]-w) dp[u][j]=max(dp[u][j],dp[u][j−k]+dp[v][k]−w)

be careful:

The upper limit of this tree knapsack capacity is the number of child nodes (it needs to be returned in the function (set to int type and return the number of child nodes))

When enumerating decisions, it is the subtree size t t t and backpack space j j j is min, i.e m i n ( t , j ) min(t,j) min(t,j)

Leaf nodes need to be considered separately

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 3060;
int n, m, id;
int head[MAXN], ww[MAXN], dp[MAXN][MAXN];

struct Edge{
    int to, next, w;
}E[MAXN * 2];

void addedge(int u, int v, int w)
{
    E[id].w = w;
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
}

int dfs(int u, int fa)
{
    if(u > n - m)
    {
        dp[u][0] = 0;
        dp[u][1] = ww[u];
        return 1;
    }
    dp[u][0] = 0;
    int leaf = 0;
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to, w = E[i].w;
        if(p == fa) continue;
        int t = dfs(p, u);
        leaf += t;
        for(int j = leaf; j >= 0; j --)
        {
            for(int k = 0; k <= min(j, t); k++)
            {
                dp[u][j] = max(dp[u][j], dp[u][j - k] + dp[p][k] - w);
            }
        }
    }
    return leaf;
}

int main()
{
    memset(head, -1, sizeof(head));
    memset(dp, -0x3f, sizeof(dp));

    cin >> n >> m;
    for(int i = 1; i <= n - m; i++)
    {
        int num;
        cin >> num;
        for(int j = 1; j <= num; j++)
        {
            int v, w;
            cin >> v >> w;
            addedge(i, v, w);
            addedge(v, i, w);
        }
    }

    for(int j = n - m + 1; j <= n; j++) cin >> ww[j];

    dfs(1, -1);
    int ans = -1;

    for(int i = m; i >= 0; i--)
    {
        if(dp[1][i] >= 0) {
            ans = i;
            printf("%d\n", ans);
            break;
        }
    }

    return 0;
}

[P2899 USACO08JAN]Cell Phone Network G - New Ecology of computer science education in Luogu (luogu.com.cn) (state machine)

Cover a tree. For any point, just ensure that its parent node and child node are covered.

State machine idea

d p [ u ] [ 0 ] dp[u][0] dp[u][0] indicates that it is overwritten

d p [ u ] [ 1 ] dp[u][1] dp[u][1], indicating that its child nodes are overwritten

d p [ u ] [ 2 ] dp[u][2] dp[u][2], indicating that its parent node is overwritten

dp[u][0] = sum(min(dp[p][0], dp[p][1], dp[p][2]));
dp[u][1] = dp[x][0] + sum(min(dp[p][0], dp[p][1]));
dp[u][2] = sum(min(dp[p][0], dp[p][1]));
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 10010;
int n, m, id;
int head[MAXN], ww[MAXN], dp[MAXN][3];

struct Edge{
    int to, next;
}E[MAXN * 2];

void addedge(int u, int v)
{
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
}

void dfs(int u, int fa)
{
    int sum = 0;
    dp[u][0] = 1;
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == fa) continue;
        dfs(p, u);
        dp[u][0] += min(dp[p][0], min(dp[p][1], dp[p][2]));
        dp[u][2] += min(dp[p][0], dp[p][1]);
        sum += min(dp[p][0], dp[p][1]);
    }
    dp[u][1] = 1e9;
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        dp[u][1] = min(dp[u][1], sum - min(dp[p][1], dp[p][0]) + dp[p][0]);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    cin >> n;
    for(int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        addedge(u, v);
        addedge(v, u);
    }

    dfs(1, -1);
    printf("%d\n", min(dp[1][0], dp[1][1]));

    return 0;
}

[P4084 Usaco17dec] barnpainting G - New Ecology of computer science education in Luogu (luogu.com.cn)

Dye a tree. It's a little dyed.

d p [ i ] [ 1 / 2 / 3 ] dp[i][1/2/3] dp[i][1/2/3] stands for the second i i i nodes, dyed to the 1st / 2nd / 3rd color.

dp[i][1] *= (dp[p][2] + dp[p][3])

dp[i][2] *= (dp[p][1] + dp[p][3])

dp[i][3] *= (dp[p][1] + dp[p][2])

Note initialization

Only those dyed d p [ u ] [ c o l o r [ u ] ] = 1 dp[u][color[u]]=1 dp[u][color[u]]=1

Those that have not been dyed are 1

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;
typedef long long ll;
const int MAXN = 100100, mod = 1e9 + 7;
int n, m, id;
ll ans;
ll head[MAXN], dp[MAXN][4], color[MAXN];

struct Edge{
    int to, next;
}E[MAXN * 2];

void addedge(int u, int v)
{
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
}

void dfs(int u, int fa)
{
    if(color[u])
    {
        dp[u][color[u]] = 1;
    }
    else {
        for(int i = 1; i <= 3; i++)
        {
            dp[u][i] = 1;
        }
    }
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == fa) continue;
        dfs(p, u);
        dp[u][1] = dp[u][1] * ((dp[p][2] + dp[p][3]) % mod) % mod;
        dp[u][2] = dp[u][2] * ((dp[p][1] + dp[p][3]) % mod) % mod;
        dp[u][3] = dp[u][3] * ((dp[p][1] + dp[p][2]) % mod) % mod;
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    cin >> n >> m;
    for(int i = 1; i < n; i++)
    {
        int u, v;
        cin >> u >> v;
        addedge(u, v);
        addedge(v, u);
    }
    for(int i = 1; i <= m; i++)
    {
        int k, c;
        cin >> k >> c;
        color[k] = c;
    }
    dfs(1, -1);
    ans = (dp[1][1] + dp[1][2] + dp[1][3]) % mod;
    printf("%lld\n", ans);
    return 0;
}

[P2986 USACO10MAR]Great Cow Gathering G - New Ecology of computer science education in Luogu (luogu.com.cn) (change DP)

First find the sum of the paths of node 1

Then according to f [ p ] = f [ u ] − s z [ p ] ∗ w + ( c n t − s z [ p ] ) ∗ w f[p]=f[u]-sz[p]*w+(cnt-sz[p])*w f[p]=f[u]−sz[p]∗w+(cnt−sz[p])∗w

Traverse all the minimum values to find the answer

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;
typedef long long ll;
const int MAXN = 100100, mod = 1e9 + 7;
ll n, m, id, cnt;
ll head[MAXN], f[MAXN], num[MAXN], sz[MAXN];

struct Edge{
    int to, next, w;
}E[MAXN * 2];

void addedge(int u, int v, int w)
{
    E[id].to = v;
    E[id].next = head[u];
    E[id].w = w;
    head[u] = id++;
}

void dfs1(int u, int fa)
{
    sz[u] = num[u];
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to, w = E[i].w;
        if(p == fa) continue;
        dfs1(p, u);
        sz[u] += sz[p];
        f[u] += (f[p] + sz[p] * w);
    }
}

void dfs2(int u, int fa)
{
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to, w = E[i].w;
        if(p == fa) continue;
        f[p] = f[u] - sz[p] * w + (cnt - sz[p]) * w;
        dfs2(p, u);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    cin >> n;
    for(int i = 1; i <= n; i++) {
        scanf("%lld", &num[i]);
        cnt += num[i];
    }
    for(int i = 1; i < n; i++)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        addedge(u, v, w);
        addedge(v, u, w);
    }
    dfs1(1, -1);
    dfs2(1, -1);

    ll ans = f[1];
    for(int i = 1; i <= n; i++) ans = min(ans, f[i]);
    cout << ans << endl;

    return 0;
}

[P4438 HNOI/AHOI2018] road - New Ecology of computer science education in Luogu (luogu.com.cn)

Read the questions first.

This question gives a tree. In this tree, there are no sons in the countryside, while there are two sons in the city. So?

According to the meaning of the title, it can be set f i , j , k f_{i,j,k} fi,j,k, represents the minimum inconvenient value of the subtree with i as the node when walking from the root node to the point with sequence number i and passing through j left unpaved roads (the road to the left son) and k right unpaved roads (the road to the right son).

As soon as this array is opened, it has a strong smell of DP. When i is a country, i can obviously find it directly f i , j , k = c i ( a i + j ) ( b i + k ) f_{i,j,k}=c_i(a_i+j)(b_i+k) fi,j,k​=ci​(ai​+j)(bi​+k) . If ii is a city, then l i l_i li is the left son of i, r i r_i ri is the right son of i. There are two situations:

1. Build a road to the left. Then there are:

f i , j , k = f l i , j , k + f r i , j , k + 1 f_{i,j,k}=f_{l_i,j,k}+f_{r_i,j,k+1} fi,j,k​=fli​,j,k​+fri​,j,k+1​

2. Build a road to the right. Then there are:

f i , j , k = f l i , j + 1 , k + f r i , j , k f_{i,j,k}=f_{l_i,j+1,k}+f_{r_i,j,k} fi,j,k​=fli​,j+1,k​+fri​,j,k​

The solution can be obtained only when min is taken.

The final answer? Certainly f 1 , 0 , 0 f_{1,0,0} f1,0,0.

#include<cstdio>  
#include<cstring>  
#include<algorithm>  
#define LL long long  
using namespace std;  
    int n;  
    struct node{LL x,y,z;} a[20010];  
    int son[20010][5];  
    LL f[20010][45][45];  
LL dfs(int x,int p,int q)  
{  
    if(x>=n) return a[x-n+1].z*(a[x-n+1].x+p)*(a[x-n+1].y+q);  
    if(f[x][p][q]!=f[n+1][41][41]) return f[x][p][q];  
    return f[x][p][q]=min(dfs(son[x][0],p,q)+dfs(son[x][1],p,q+1),dfs(son[x][1],p,q)+dfs(son[x][0],p+1,q));  
}  
int main()  
{  
    int x,y;  
    scanf("%d",&n);  
    memset(f,63,sizeof(f));  
    for(int i=1;i<n;i++)  
    {  
        scanf("%d %d",&x,&y);  
        if(x<0) x=-x+n-1;  
        if(y<0) y=-y+n-1;  
        son[i][0]=x;  
        son[i][1]=y;  
    }  
    for(int i=1;i<=n;i++)  
        scanf("%lld %lld %lld",&a[i].x,&a[i].y,&a[i].z);  
    printf("%lld",dfs(1,0,0));  
}

[P3047 USACO12FEB]Nearby Cows G - New Ecology of computer science education in Luogu (luogu.com.cn) (change DP)

Give you a tree of n points with weights. For each node, find the sum of the weights of all nodes whose distance does not exceed k m i m_i mi​

f [ u ] [ i ] f[u][i] f[u][i] represents the node u u u. And the distance is i i Sum of weights of i

dfs1 calculates the weight of the root node

dfs2 is calculated according to the principle of inclusion and exclusion, and is far from the ion node p p p distance is x x The point of x is the distance from the parent node u u u distance is x − 1 x-1 Point of x − 1, but p p The subtree of p is calculated repeatedly.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;


typedef long long ll;
const int MAXN = 100100, mod = 1e9 + 7;
int n, m, id, cnt;
int head[MAXN], f[MAXN][25], c[MAXN], d[MAXN];


struct Edge{
    int to, next;
}E[MAXN * 2];


void addedge(int u, int v)
{
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
}


void dfs1(int u, int fa)
{
    f[u][0] = c[u];
    d[u] = max(d[u], d[fa] + 1);
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == fa) continue;
        dfs1(p, u);
        for(int j = 1; j <= m; j++) f[u][j] += f[p][j - 1];
    }
}


void dfs2(int u, int fa)
{
    for(int i = head[u];i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == fa) continue;
        for(int j = m; j >= 2; j--)
        {
            f[p][j] -= f[p][j - 2];
        }
        for(int j = 1; j <= m; j++)
        {
            f[p][j] += f[u][j - 1];
        }
        dfs2(p, u);
    }
}


int main()
{

    memset(head, -1, sizeof(head));
    cin >> n >> m;
    for(int i = 1; i < n; i++)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        addedge(u, v);
        addedge(v, u);
    }
    for(int i = 1; i <= n; i++) scanf("%d", &c[i]);

    dfs1(1, -1);
    dfs2(1, -1);

    for(int i = 1; i<= n; i++)
    {
        int ans = 0;
        for(int j = 0; j <= m; j++)
        {
            ans += f[i][j];
        }
        printf("%d\n", ans);
    }


    return 0;
}

[P2585 ZJOI2006] three color binary tree - Luogu | new ecology of Computer Science Education (luogu.com.cn)

f [ u ] [ 0 / 1 ] f[u][0/1] f[u][0/1] indicates the node u u u maximum value of dyed green or not dyed green

f [ u ] [ 0 ] = f [ l e f t [ u ] ] [ 1 ] + f [ r i g h t [ u ] [ 1 ] ] f[u][0]=f[left[u]][1]+f[right[u][1]] f[u][0]=f[left[u]][1]+f[right[u][1]]

f [ u ] [ 1 ] = m a x ( f [ l e f t [ u ] ] [ 0 ] + f [ r i g h t [ u ] ] [ 1 ] , f [ l e f t [ u ] ] [ 1 ] + f [ r i g h t [ u ] ] [ 0 ] ) f[u][1]=max(f[left[u]][0]+f[right[u]][1],f[left[u]][1]+f[right[u]][0]) f[u][1]=max(f[left[u]][0]+f[right[u]][1],f[left[u]][1]+f[right[u]][0])

For the case where the ith node is not dyed green, there is no case where both nodes are not dyed green

However, according to the drawer principle, if three dots are dyed with two colors, there must be two dots with the same color.

Not in accordance with the meaning of the question

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;

const int N = 1000000;
int n, ch[N][2], f[N][3], g[N][3], tot = 0;
string a;

int build()
{
	int now = ++tot; //New node number
	if(a[now - 1] == '2') ch[now][0] = build(), ch[now][1] = build();
	else if(a[now - 1] == '1') ch[now][0] = build();
	return now; //Return node number
}

void dfs(int x)
{
	int l = ch[x][0], r = ch[x][1];
	if(l) dfs(l); if(r) dfs(r); //I have two sons
    if(!l && !r) //Leaf node initialization
    	f[x][0] = g[x][0] = 1, f[x][1] = f[x][2] = g[x][1] = g[x][2] = 0;
	//Transfer equation
	f[x][0] = max(f[l][1] + f[r][2], f[l][2] + f[r][1]) + 1;
	f[x][1] = max(f[l][0] + f[r][2], f[l][2] + f[r][0]);
	f[x][2] = max(f[l][0] + f[r][1], f[l][1] + f[r][0]);
	g[x][0] = min(g[l][1] + g[r][2], g[l][2] + g[r][1]) + 1;
	g[x][1] = min(g[l][0] + g[r][2], g[l][2] + g[r][0]);
	g[x][2] = min(g[l][0] + g[r][1], g[l][1] + g[r][0]);
}

int main()
{
	cin >> a; n = a.size(); //The number of nodes in the tree is equal to the length of the string
	memset(ch, 0, sizeof(ch)); //If a node does not have this son, the number of this position is 0
	f[0][0] = f[0][1] = f[0][2] = 0; //Initialize node number 0
	dfs(build());
	printf("%d", max(f[1][0], max(f[1][1], f[1][2]))); //The maximum value is taken for the three dyes
	printf(" %d", min(g[1][0], min(g[1][1], g[1][2]))); //The minimum value shall be taken for the three dyes
	return 0;
} 

[P2458 SDOI2006] security guard - New Ecology of computer science education in Luogu (luogu.com.cn)

Not written

Template question

The problem of finding the minimum point cover on a tree

f [ u ] [ 0 / 1 / 2 ] f[u][0/1/2] f[u][0/1/2] indicates that it is overwritten, the child node is overwritten, and the parent node is overwritten

UVA1218 Perfect Service - New Ecology of computer science education in Luogu (luogu.com.cn) (state machine)

Not written

A network has N nodes connected by N-1 edges, and each node is a server or client. If node u is a client, it means that there is only one server in all points to which u is connected. The minimum number of servers is required to meet the requirements.

d p [ u ] [ 0 ] dp[u][0] dp[u][0] indicates u u It doesn't matter whether u is a server or not

d p [ u ] [ 1 ] dp[u][1] dp[u][1] indicates u u u is not a server, its parent node is a server, and its child node cannot be a server

d p [ u ] [ 2 ] dp[u][2] dp[u][2] indicates u u u and its parent node are not servers, and its child nodes must have one

d p [ u ] [ 0 ] = s u m ( m i n ( d p [ p ] [ 0 ] , d p [ p ] [ 1 ] ) + + 1 dp[u][0] = sum(min(dp[p][0], dp[p][1])+ + 1 dp[u][0]=sum(min(dp[p][0],dp[p][1])++1

d p [ u ] [ 1 ] = s u m ( d p [ p ] [ 2 ] ) dp[u][1]=sum(dp[p][2]) dp[u][1]=sum(dp[p][2])

d p [ u ] [ 2 ] = s u m ( d p [ p ] [ 2 ] ) + d p [ x ] [ 0 ] − d p [ x ] [ 2 ] dp[u][2]=sum(dp[p][2])+dp[x][0]-dp[x][2] dp[u][2]=sum(dp[p][2])+dp[x][0]−dp[x][2]

P1272 reconstruction road - New Ecology of computer science education in Luogu (luogu.com.cn) (tree backpack)

d p [ k ] [ i ] [ j ] dp[k][i][j] dp[k][i][j] denotes by i i i is the subtree of the root, before k k Among the k sons, the size of the separated one is j j The minimum number of operations on the subtree of j.

d p [ k + 1 ] [ i ] [ j ] = m i n ( d p [ k ] [ i ] [ j − t ] + d p [ f u l l s o n [ v ] ] [ v ] [ t ] ) dp[k+1][i][j]=min(dp[k][i][j-t]+dp[full_son[v]][v][t]) dp[k+1][i][j]=min(dp[k][i][j−t]+dp[fulls​on[v]][v][t])

$j=m->1 $

t = 1 − > j t=1->j t=1−>j

$ dp[i][j]=min(dp[i][j-t]+dp[v][t]-2)$

initialization:

d p [ u ] [ 0 ] = 0 dp[u][0]=0 dp[u][0]=0

d p [ u ] [ 1 ] = d u [ u ] dp[u][1]=du[u] dp[u][1]=du[u] and u u u number of connected edges

The reason for subtracting 2: when we update u through v, we need to keep the edge U - > v, but

Think about our initialization. What are we doing f [ u ] [ k ] f[u][k] Cut off this edge once in f[u][k]

Again f [ v ] [ j − k ] f[v][j-k] This side is cut off once in f[v][j − k], so it needs to be added back

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=201;
struct Edge{
    int to,next;
}e[N*2];
int du[N],a[N],dp[N][N];
int n,k,res=INF,EdgeCnt=0;
void addedge(int u,int v){
    int p=++EdgeCnt;
    e[p].to=v;e[p].next=a[u];
    a[u]=p;
}
void dfs(int u,int fa){
    dp[u][1]=du[u];
    for (int p=a[u];p;p=e[p].next){
        int v=e[p].to;
        if (v!=fa){
            dfs(v,u);
            for (int j=k;j>=1;j--)
                for (int k=1;k<=j;k++)
                    dp[u][j]=min(dp[u][j],dp[u][j-k]+dp[v][k]-2);
        }
    }
    res=min(res,dp[u][k]);
}
int main(){
    scanf("%d%d",&n,&k);
    memset(dp,0x3f,sizeof(dp));
    for (int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
        du[u]++;du[v]++;
    }
    dfs(1,0);
    printf("%d",res);
    return 0;
}

[P3177 HAOI2015] tree dyeing - Luogu | new ecology of Computer Science Education (luogu.com.cn)

A tree has a total of n n n points, k k k dots are black, n − k n-k n − k points are white. Ask the sum of the maximum distance between two black points and two white points.

Consider the contribution of each edge. If two points of the same color are on both sides of the edge, the contribution is w w w. Otherwise, the contribution is 0.

use s z [ ] sz[] sz [] maintain the number of nodes in the subtree

Contribution for t o t ∗ w tot*w tot∗w

t o t = k ∗ ( m − k ) + ( s z [ p ] − k ) ∗ ( n − m − ( s z [ p ] − k ) ) tot=k*(m-k)+(sz[p]-k)*(n-m-(sz[p]-k)) tot=k∗(m−k)+(sz[p]−k)∗(n−m−(sz[p]−k))

d p [ u ] [ k ] = m a x ( d p [ u ] [ k ] , d [ u ] [ j − k ] + d p [ p ] [ k ] + t o t ∗ w ) dp[u][k] = max(dp[u][k], d[u][j - k] + dp[p][k] + tot * w) dp[u][k]=max(dp[u][k],d[u][j−k]+dp[p][k]+tot∗w)

A lot of pits!!!

j = m i n ( s z [ u ] , m ) − > 0 j = min(sz[u], m) ->0 j=min(sz[u],m)−>0

k = m i n ( j , s z [ p ] ) − > 1 k=min(j,sz[p])->1 k=min(j,sz[p])−>1

ensure u u Child node of u p p p must be chosen

Every time you update d p [ u ] [ k ] dp[u][k] dp[u][k] definitely d p [ u ] [ j − k ] dp[u][j-k] dp[u][j − k] can only be updated when it is updated

Summary: tree backpacks generally need maintenance s z [ ] sz[] sz[]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;


typedef long long ll;
const int MAXN = 2020;
int n, m, id;
int head[MAXN], sz[MAXN];
ll f[MAXN][MAXN];

struct Edge{
    int to, next, w;
}E[MAXN * 2];

void addedge(int u, int v, int w)
{
    E[id].w = w;
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
}


void dfs(int u, int fa)
{
    sz[u] = 1;
    f[u][0] = f[u][1] = 0;
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to, w = E[i].w;
        if(p == fa) continue;
        dfs(p, u);
        sz[u] += sz[p];
        for(int j = min(m, sz[u]); j >= 0; j--)
        {
            for(int k = 0; k <= min(sz[p], j); k++)
            {
                if(f[u][j - k] == -1) continue;
                ll tot = (ll)k * (m - k) + (ll)(sz[p] - k) * (n - m - (sz[p] - k));
                f[u][j] = max(f[u][j], f[u][j - k] + f[p][k] + (ll)tot * w);
            }
        }
    }
}

int main()
{

    memset(head, -1, sizeof(head));
    cin >> n >> m;
    if(n - m < m) m = n - m;
    for(int i = 1; i < n; i++)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        addedge(u, v, w);
        addedge(v, u, w);
    }
    memset(f, -1, sizeof(f));
    dfs(1, -1);
    printf("%lld\n", f[1][m]);
    return 0;
}

[P3174 HAOI2009] caterpillar - Luogu | new ecology of Computer Science Education (luogu.com.cn)

Find the maximum value of the point covered by a chain and its connected edge

Start from the root node to maintain the longest chain and the second longest chain. Pay attention to the special judgment of each case and the preciseness of classification discussion

Real time update a n s ans ans

s z [ u ] sz[u] sz[u] maintenance to u u u is the longest number of caterpillars in the root

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;
const int MAXN = 3e5 + 60;
int head[MAXN], sz[MAXN], d[MAXN];
int id, n, m, ans;
struct Edge{
    int to, next;
}E[MAXN * 2];

void addedge(int u, int v)
{
    E[id].to = v;
    E[id].next = head[u];
    head[u] = id++;
    return;
}

void dfs(int u, int fa)
{
    sz[u] = 0;
    int mx0 = 0, mx1 = 0;
    for(int i = head[u]; i != -1; i = E[i].next)
    {
        int p = E[i].to;
        if(p == fa) continue;
        dfs(p, u);
        sz[u] = max(sz[u], sz[p]);
        if(sz[p] >= mx0) {mx1 = mx0; mx0 = sz[p];}
        else if(sz[p] > mx1) mx1 = sz[p];
    }
    if(mx0 == 0) ans = max(ans, d[u] + 1);
    else if(mx1 == 0) ans = max(ans, mx0 + d[u]);
    else ans = max(ans, mx0 + mx1 + d[u] - 1);
    sz[u] += ((d[u] == 1) ? 1 : (d[u] - 1));
}

int main()
{
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= m; i++)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        addedge(u, v);
        addedge(v, u);
        d[u] ++;
        d[v] ++;
    }
    dfs(1, -1);

    printf("%d\n", ans);

    return 0;
}

P6554 Promises I Can't Keep - New Ecology of computer science education in Luogu (luogu.com.cn) (change DP)

The root of the first dfs is root, f u f_u fu indicates path and, w u w_u wu = represents the weight of the point, c n t u cnt_u cntu , denotes u u u is the number of leaf nodes in the subtree of the root

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e6 + 5;

int head[N], ver[N], net[N], idx;
int cnt[N], dg[N], root;
bool is[N];
double sum[N], f[N], w[N];

void add(int a, int b)
{
    net[idx] = head[a];
    ver[idx] = b;
    head[a] = idx++;
}

void dfs1(int u, int fa)
{
    for (int i = head[u]; ~i; i = net[i])
    {
        int v = ver[i];
        if (v == fa)
            continue;
        dfs1(v, u);
        cnt[u] += cnt[v];
        sum[u] += sum[v] + w[u] * cnt[v];
    }    
    if (!cnt[u])
        sum[u] = w[u], cnt[u] = 1, is[u] = true;
}

void dfs2(int u, int fa)
{
    for (int i = head[u]; ~i; i = net[i])
    {
        int v = ver[i];
        if (v == fa)
            continue;
        if (is[v])
            f[v] = f[u] - w[u] + (cnt[root] - 2) * w[v];
        else
            f[v] = f[u] - w[u] * cnt[v] + (cnt[root] - cnt[v]) * w[v];
        dfs2(v, u);
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    int n;
    scanf("%d", &n);
    for (int i = 1; i < n; i++)
    {
        int u, v;
        scanf("%d%d", &u, &v);
        add(u, v), add(v, u);
        dg[u]++, dg[v]++;
    }
    for (int i = 1; i <= n; i++)
    {
        scanf("%lf", &w[i]);      
        if (dg[i] > 1)
            root = i;//Find the root node
    }
    dfs1(root, 0);
    f[root] = sum[root];
    dfs2(root, 0);
    double ans = -1e13;
    for (int i = 1; i <= n; i++)
        ans = max(ans, (double)f[i] / (cnt[root] - is[i]));//Calculate expectations
    printf("%.4lf", ans);
    return 0;
}

Posted by Installer on Thu, 23 Sep 2021 02:05:46 -0700