Loading

换根DP

换根法思路:

  1. 自下而上递推;
  2. 自上而下递推。

P3478 [POI2008] STA-Station

题目描述:

image

思路:

个节点 \(u\) 为根的子树大小 \(s[u]\)

然后我们设 \(f[i]\) 为以 \(i\) 为根时所有节点的深度之和,\(j\)\(i\) 的子节点。

那么对于所有 \(j\) 的子节点,深度都减 \(1\),所以总共减少了 \(s[j]\)

对于所有不是 \(j\) 的子节点的节点,深度都加 \(1\) ,所以总共加了 \(n - s[j]\)

所以 \(f[j] = f[i] - s[j] + n - s[j] = f[i] + n - 2 \times s[j]\)

最后取 \(\max\) 即可。

代码:

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

using namespace std;
using i64 = long long;

const int N = 1000010;

struct Edge {
	int to, next;
}e[N * 2];

int head[N], idx;

void add(int a, int b) {
	idx++;
	e[idx].to = b;
	e[idx].next = head[a];
	head[a] = idx;
}

int n;
int sz[N];

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

int f[N];
int maxv, ans;

void dfs2(int u, int fa) {
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		if (to == fa) continue;
		f[to] = f[u] + n - 2 * sz[to];
		dfs2(to, u);
	}
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	cin >> n;
	for (int i = 1; i < n; i++) {
		int x, y;
		cin >> x >> y;
		add(x, y);
		add(y, x);
	}
	dfs1(1, 0);
	for (int i = 1; i <= n; i++) f[1] += sz[i];
	dfs2(1, 0);
	for (int i = 1; i <= n; i++) if (f[i] > maxv) maxv = f[i], ans = i;
	cout << ans << '\n';
	return 0;
}

AcWing 287. 积蓄程度 / POJ 3585 Accumulation Degree

题目描述:

image

思路:

\(d[i]\) 表示 \(i\) 点可以向下传递的最大流量。

\(f[i]\) 表示 \(i\) 点可以向外传递的最大流量(包括向下流的流量和向上流的流量)。

\(\text{dfs}\) 一遍求出 \(d\)

有:

如无特殊说明 \(to\) 表示 \(i\) 的子节点,\(edge(i, to)\) 表示 \(i\)\(to\) 这条边的最大流量。

\(d[i] = \sum \min(d[to], edge(i, to))\)

\(f[to] = \min(edge(i, to), f[i] - \min(edge(i, to), d[to]))\)

同时注意如果根的度为 \(1\)(如下图) ,那么计算它的子节点时要采用公式:

\(f[i] = f[to] + edge(i, to)\)

image

代码:

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

using namespace std;

const int N = 200010, INF = 0x3f3f3f3f;

struct Edge {
	int to;
	int next;
	int w;
}e[N * 2];

int head[N], idx, deg[N];

void add(int a, int b, int c) {
	idx++;
	e[idx].to = b;
	e[idx].next = head[a];
	e[idx].w = c;
	head[a] = idx;
}

int n;
int f[N], d[N];

int dfs1(int u, int fa) {
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		if (to == fa) continue;
		d[u] += min(dfs1(to, u), e[i].w);
	}
	if (deg[u] == 1) return INF;
	return d[u];
}

void dfs2(int u, int fa) {
	for (int i = head[u]; i; i = e[i].next) {
		int to = e[i].to;
		if (to == fa) continue;
		if (deg[u] == 1) f[to] = d[to] + e[i].w;
		else f[to] = d[to] + min(e[i].w, f[u] - min(d[to], e[i].w));
		dfs2(to, u);
	}
}

void solve() {
	idx = 0;
	memset(head, 0, sizeof(head));
	memset(deg, 0, sizeof(deg));
	memset(f, 0, sizeof(f));
	memset(d, 0, sizeof(d));
	cin >> n;
	for (int i = 1; i < n; i++) {
		int x, y, z;
		cin >> x >> y >> z;
		add(x, y, z);
		add(y, x, z);
		deg[x]++;
		deg[y]++;
	}
	dfs1(1, 0);
	f[1] = d[1];
	dfs2(1, 0);
	int ans = 0;
	for (int i = 1; i <= n; i++) ans = max(ans, f[i]);
	cout << ans << '\n';
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);

	int T;
	cin >> T;
	while (T--) solve();

	return 0;
}

P2986 [USACO10MAR] Great Cow Gathering G

题目描述:

image

思路:

\(f[u]\) 表示如果选择集会的地点为 \(u\) 点时所需时间。

根据经验,我们可以得到

\(f[to] = f[u] - to\text{节点下所有奶牛个数} \times w(u, to) + (奶牛总数 - to\text{节点下所有奶牛个数}) \times w(u, to)\)

代码:

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int N = 100010, M = 200010;

struct Edge {
    int to;
    int next;
    int w;
}e[M];

int head[N], idx;

void add(int a, int b, int c) {
    idx++;
    e[idx].to = b;
    e[idx].next = head[a];
    e[idx].w = c;
    head[a] = idx;
}

int sz[N];
int cnt[N];

int dfs(int u, int fa) {
    int all = 0;
    sz[u] = cnt[u];
    for (int i = head[u]; i; i = e[i].next) {
        int to = e[i].to;
        if (to == fa) continue;
        all += dfs(to, u);
        sz[u] += sz[to];
        all += sz[to] * e[i].w;
    }
    return all;
}

int f[N], n, get_n;

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

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n;
    for (int i = 1; i <= n; i++) cin >> cnt[i], get_n += cnt[i];
    for (int i = 1; i < n; i++) {
        int x, y, z;
        cin >> x >> y >> z;
        add(x, y, z);
        add(y, x, z);
    }

    f[1] = dfs(1, 0);
    dfs2(1, 0);

    int min_sum = 0x3f3f3f3f3f3f3f3f;
    for (int i = 1; i <= n; i++) {
        if (min_sum > f[i]) {
            min_sum = f[i];
            // ans = i;
        }
    }
    cout << min_sum << '\n';
    return 0;
}

P3047 [USACO12FEB]Nearby Cows G

题目描述

image

思路

使用换根DP,

\(dp[i][j]\) 表示以 \(i\) 为根节点的子树中深度小于等于 \(j\) 的点的权值之和。

\(f[i][j]\) 表示将第 \(i\) 个点作为整棵树的根节点深度小于等于 \(j\) 的点的权值之和。

image

有:

\[\begin{cases} dp[u][k] = \sum dp[to][k - 1] \\ f[to][j] = dp[to][j] - dp[to][j - 2] + f[u][j - 1] \end{cases} \]


\(f[to][j] = dp[to][j] - dp[to][j - 2] + f[u][j - 1]\) 表示:

image

代码:

#include <bits/stdc++.h>

using namespace std;

const int N = 100010, M = 30;

int w[N];

struct Edge {
	int to;
	int next;
}e[N * 2];

int head[N], idx;

void add(int a, int b) {
	idx++;
	e[idx].to = b;
	e[idx].next = head[a];
	head[a] = idx;
}

int n, k;
int dp[N][M];
int f[N][M];

void dfs(int u, int fa) {
    for (int i = 0; i <= k; i++) dp[u][i] = w[u];
    for (int i = head[u]; i; i = e[i].next) {
        int to = e[i].to;
        if (to == fa) continue;
        dfs(to, u);
        for (int i = 1; i <= k; i++) dp[u][i] += dp[to][i - 1];
    }
}

void dfs2(int u, int fa) {
    for (int i = head[u]; i; i = e[i].next) {
        int to = e[i].to;
        if (to == fa) continue;
        f[to][1] = dp[to][1] + w[u];
        for (int j = k; j >= 2; j--) f[to][j] = f[u][j - 1] - dp[to][j - 2] + dp[to][j];
        dfs2(to, u);
    }
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	
    cin >> n >> k;
    for (int i = 1; i < n; i++) {
        int a, b;
        cin >> a >> b;
        add(a, b);
        add(b, a);
    }
    for (int i = 1; i <= n; i++) cin >> w[i];
    dfs(1, 0);
    memcpy(f[1], dp[1], sizeof(f[1]));
    dfs2(1, 0);
    for (int i = 1; i <= n; i++) cout << f[i][k] << '\n';
	return 0;
}
posted @ 2023-07-28 17:48  SunnyYuan  阅读(9)  评论(0编辑  收藏  举报