P3128 [USACO15DEC] Max Flow P 题解

题目传送门

简化题意:

给出一棵 N 个节点的树和 K 次修改,每次修改对路径 [s,t] 上的所有点权加 1,求 K 次修改后权值最大的点。

分析:

树上差分板子。

先回顾一下序列差分:

序列差分经典题就是进行若干次修改,最终求出整个序列。

因为差分是前缀和的逆操作,所以对差分数组进行前缀和就可以还原原数组。

假设我们要对 [l,r]k,我们只需要让序列 lkr+1k 即可(在前缀和后,[r+1,n] 的部分加了 k 又减了 k,相当于没加)。

树上差分类似,一般是对树上的若干链进行修改操作,最终求整棵树的所有点权。

我们首先对这棵树确定一个根(一般为 1),那么树上的前缀和,可以认为就是自底向上把父节点的权值加上所有子节点的权值。

所以,如果我们要修改路径 [l,r] 上的节点,我们先将 l,r 的权值分别加 1(题目要求)。但是这样,这两个节点的 LCA 就多算了了 1,LCA 的所有祖先多算了 2。解决方案就是将 LCA(u,v) 点权减 1,再将 LCA(u,v) 的父节点加 1,就完美解决了问题。

求 LCA 的方法很多,具体参考本文,下方代码使用了树剖求法。

#include<bits/stdc++.h>
using namespace std;
const int N = 5e4 + 9;
struct edge{
    int to,nex;
} e[N << 1];
int ecnt,head[N];
int top[N],dep[N];
int fa[N],weight_child[N],siz[N];
void addedge(int u,int v){
    ecnt++;
    e[ecnt] = (edge){v,head[u]};
    head[u] = ecnt;
}
int val[N];
int n,k,ans;
void dfs1(int cur,int father){
    fa[cur] = father;
    siz[cur] = 1;
    dep[cur] = dep[father] + 1;
    for(int i = head[cur];i;i = e[i].nex){
        int v = e[i].to;
        if(v != father){
            dfs1(v,cur);
            siz[cur] += siz[v];
            if(siz[v] > siz[weight_child[cur]])
                weight_child[cur] = v;
        }
    }
}
void dfs2(int cur,int link_top){
    top[cur] = link_top;
    if(weight_child[cur]){
        dfs2(weight_child[cur],link_top);
        for(int i = head[cur];i;i = e[i].nex){
            int v = e[i].to;
            if(v != fa[cur] && v != weight_child[cur])
                dfs2(v,v);
        }
    }
}
int LCA(int u,int v){
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]])
            swap(u,v);
        u = fa[top[u]];
    }
    return (dep[u] < dep[v]) ? u : v;
}

void dfs3(int cur){
    for(int i = head[cur];i;i = e[i].nex){
        int v = e[i].to;
        if(v != fa[cur]){
            dfs3(v);
            val[cur] += val[v];
        }
    }
    ans = max(ans,val[cur]);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin >> n >> k;
	for(int i = 1;i <= n - 1;i++){
        int x,y;
        cin >> x >> y;
        addedge(x,y);
        addedge(y,x);
    }
    dep[1] = 1;
    dfs1(1,-1);
    dfs2(1,-1);
    for(int i = 1;i <= k;i++){
    	int x,y;
    	cin >> x >> y;
    	val[x]++;val[y]++;
    	int lca = LCA(x,y);
    	val[lca]--;val[fa[lca]]--;
	}
	dfs3(1);
	cout << ans;
    return 0;
}
posted @   5t0_0r2  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示