P3128 [USACO15DEC]最大流Max Flow

本文介绍了一种在树结构上进行区间修改和查询的有效算法——树上差分。通过实例详细展示了如何利用树上差分实现特定路径上节点值的高效修改与查询,以及如何处理边权问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

P3128 [USACO15DEC]最大流Max Flow

对,这是一道最大流的题目qwq

树上跑最大流,没错

也就是跑最小割
你看名字里都有最大流,为什么不能跑最大流qwq..............................
编不下去了

跑树上差分。

就像数组上可以进行差分一样,树上也可以进行差分。适用于树上两点之间的路径操作,和极少数的查询

适用于维护路径长可以进行结合律的操作

比如说我们要在树上进行两点之间的路径上的节点值加一个数,最后输出所有节点值之和。

我们可以进行树链剖分,可是使用树链剖分有些大材小用了,而且常数巨大

我们就可以使用差分

553b5N.png

比如说这么一棵树

我们要使得8到11的路径上的点加3

553MIH.png

我们就可以设一个数组,\(t[i]\)表示这棵树的差分数组,\(st[i][j]\)表示树上的倍增数组

然后我们令\(t[8]+=3,t[11]+=3,t[lca_{(8,11)}]-=3,t[st[lca_{(8,11)}][0]]-=3\)

553GJh.png

然后我们将差分数组使用以下程序进行整理整理,然后进行查询。


void DFS(int now,int fa)
{
    int pas=0;
    for(int i=head[now];i;i=line[i].nxt)
        if(line[i].p!=fa)
        {
            DFS(line[i].p,now);
            t[now]+=t[line[i].p];
        }
}

将所有点上的值递归加起来,就成了下图的情况。成功实现了O(1)修改,O(N)查询

553jTS.png


而对于维护边权呢?我们可以将边权下放至所连点中深度最深的点上去

然后将差分时的\(t[st[lca_{(a,b)}][0]]-=add,t[lca_{(a,b)}]-=add\)变为\(t[lca_{(a,b)}]-add*2\)就可以了,树链剖分也可以将边权下放至点

此题\(code\)

#include<cstdio>
#include<algorithm>
#include<iostream>
using std::swap;
using std::max;
const int maxn=50100;
struct node
{
    int p;
    int nxt;
};
node line[maxn<<1];
int head[maxn],tail;
void add(int a,int b)
{
    line[++tail].p=b;
    line[tail].nxt=head[a];
    head[a]=tail;
}
int st[maxn][20];
int log[maxn];
int dep[maxn];
int ans,t[maxn];
void dfs(int now,int fa)
{
    st[now][0]=fa;
    dep[now]=dep[fa]+1;
    for(int i=1;i<=log[dep[now]];i++)
        st[now][i]=st[st[now][i-1]][i-1];
    for(int i=head[now];i;i=line[i].nxt)
        if(line[i].p!=fa)
            dfs(line[i].p,now);
}
int lca(int a,int b)
{
    if(dep[a]<dep[b])   swap(a,b);
    for(int i=log[dep[a]];i>=0;i--)
        if(dep[st[a][i]]>=dep[b])
            a=st[a][i];
    if(a==b)    return a;
    for(int i=log[dep[a]];i>=0;i--)
        if(st[a][i]!=st[b][i])
            a=st[a][i],b=st[b][i];
    return st[a][0];
}
void DFS(int now,int fa)
{
    int pas=0;
    for(int i=head[now];i;i=line[i].nxt)
        if(line[i].p!=fa)
        {
            DFS(line[i].p,now);
            t[now]+=t[line[i].p];
        }
    ans=max(ans,t[now]);
}
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=2;i<=n;i++)   log[i]=log[i>>1]+1;
    int a,b;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        add(a,b);add(b,a);
    }
    dfs(1,0);
    for(int i=1;i<=k;i++)
    {
        scanf("%d%d",&a,&b);
        t[a]++;t[b]++;
        int LCA=lca(a,b);
        t[LCA]--;   
        t[st[LCA][0]]--;
    }
    DFS(1,0);
    printf("%d",ans);
}

转载于:https://www.cnblogs.com/Lance1ot/p/9403259.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值