洛谷 P3128 [USACO15DEC] Max Flow P

洛谷 P3128 [USACO15DEC] Max Flow P

题意

给定一棵 \(n\) 个点的树,给定 \(k\) 个点对 \((u,v)\),把 \(u\)\(v\) 路径上所有点的点权加一,最后求最大点权。

思路

树上差分模版。

维护 \(a_i\) 表示每个点到根的加法标记。

对于每个点对 \((u,v)\),把 \(a_u\)\(a_v\) 加一,\(a_{LCA(u,v)}\)\(a_{fa(LCA(u,v))}\) 减一。

因为 \(u\) 到根全部加一,\(v\) 到根全部加一,会让 \(LCA(u,v)\) 多算一次(因为它本身也要加),\(fa(LCA(u,v))\) 多算一次(\(LCA(u,v)\) 减过一次)。

最后遍历一遍数计算答案即可。

代码

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 5e4 + 5;
int tot, ver[N << 1], nxt[N << 1], head[N];
int n, k, fa[N][20], de[N];
ll a[N], ans;
void add(int x, int y) {
	ver[++ tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
}
void DFS(int x) {
    de[x] = de[fa[x][0]] + 1;
    for (int i = 1; i <= 19; i ++)
        fa[x][i] = fa[fa[x][i - 1]][i - 1];
    for (int i = head[x], y; i; i = nxt[i]) {
        y = ver[i];
        if (y == fa[x][0]) continue;
        fa[y][0] = x;
        DFS(y);
    }
}
inline int LCA(int x, int y) {
    if (de[x] < de[y]) swap(x, y);
    for (int i = 19; i >= 0; i --) 
        if (de[fa[x][i]] >= de[y]) x = fa[x][i];
    if (x == y) return x;
    for (int i = 19; i >= 0; i --)
        if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
    return fa[x][0];
}
void dfs(int x) {
	for (int i = head[x], y; i; i = nxt[i]) {
		y = ver[i];
		if (y == fa[x][0]) continue;
		dfs(y); a[x] += a[y]; // 前缀和
	}
}
int main() {
	cin >> n >> k;
	for (int i = 1, u, v; i < n; i ++) {
		cin >> u >> v;
		add(u, v); add(v, u);
	}
	DFS(1);
	for (int i = 1, u, v; i <= k; i ++) {
		cin >> u >> v;
		a[u] ++, a[v] ++, a[fa[LCA(u, v)][0]] --, a[LCA(u, v)] --; // 差分
	}
	dfs(1); // 统计答案
	for (int i = 1; i <= n; i ++) ans = max(ans, a[i]);
	cout << ans << "\n";
	return 0;
}  
posted @ 2024-08-28 11:14  maniubi  阅读(1)  评论(0编辑  收藏  举报