【是题解。。。可能也是模板】树上差分luogu p3128最大流

本文介绍了树上差分的概念,并通过DFS和LCA处理如何在子树或链上加d值。讨论了两种情况:一是子树全部加d,二是链上加d。对于链上加d的情况,通过例子解析了需要在LCA及其父节点进行特殊处理的原因,最终给出了相关操作的总结和代码示例。
摘要由CSDN通过智能技术生成

关于lca的基本操作看这里ovo

了解过lca就可以看看树上差分辣

大概就是通过dfs和lca来处理的,可以实现在某一子树或某一条链上加同一个数后查询每个点的值

所以显然分为两种

第一种是把某一子树全部加d

我也觉得丑

以2为底的子树全部加d,运用差分的思想,可以在它的根节点加d,处理每个节点的值是它父节点的前缀和

比如。。这个图上2.5.6.8.9.10.11全都加d,可以2加d,对每个点递归求父节点的前缀和

第二种是求一条链上加d

依然丑

(点)5.2.6.11全部加d。考虑如果依然使用上述的方法,取父节点前缀和,那么2+d后,整颗子树全部加d

可是可是可是!8.9.10不需要加鸭!!这就比较玄学辽。

所以我们要采取另一种思路,取子节点的前缀和

先假设一种简单的情况:求(点)2 5加d。按照差分的思想,可以把5上加d,1上减d,从下往上取子节点前缀和。

       模拟这个过程。5+d,1-d。5=5+8(5上加了d),2=6+5(2上加了d),1=1+2(1上加了d又减了d,不变)。所以可以发现,从下往上真是太机智辽!!!

回到刚才的问题。5到11的一条链。显然可以使用lca把它分成5到2和2到11两条链owo。依然这么处理。

模拟一下5+d,1-d,11+d,1-d。5=5+8(5上加了d),2=6+2(2上加了d)6=6+11(6上加了d),2=6+2(2上又双叒叕加了d)(等会看1)

既然最后是高贵的粉色,说明肯定有问题。因为2上加了2个d,它只需要1个,所以能发现lca处加了2遍,于是lca要减一个d

处理完了上一个问题。继续。1=2+1(1上加一个d,减2个d)但是!!可以发现!!又粉了!!因为它不需要变,所以发现要在lca的父节点再+一个d。减2个加1个就是减1个,所以只需要lca的父节点减d

总结就是lca-d,lca的爸爸-d,两个点+d

代码是下边那个,例题是标题里的最大流(代码就是那个题)。

完啦!!!!!!撒花花

 

 

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cmath>
#include<map> 
#define ll long long
using namespace std;
struct node
{
	int nxt;
	int to;
}edg[200001];
int head[50001];
int d[50001];
int fa[50001][20];
int dep[50001];
int ans,k,n,num;
void add(int ff,int ee)
{
	edg[++num].to=ee;
	edg[num].nxt=head[ff];
	head[ff]=num;
}
void dfs(int x,int f)
{
	fa[x][0]=f;
	dep[x]=dep[f]+1;
	for(int i=1;(1<<i)<=dep[x];i++)
	{
		fa[x][i]=fa[fa[x][i-1]][i-1];
	}
	for(int i=head[x];i;i=edg[i].nxt)
	{
		if(edg[i].to!=fa[x][0])
		{
			dfs(edg[i].to,x);
		}
	}
}//lca基本操作
int lca(int x,int y)
{
	if(dep[x]>dep[y])swap(x,y);
	for(int i=log2(dep[y]);i>=0;i--)
	{
		if(dep[x]<=dep[y]-(1<<i))
		y=fa[y][i];
	}
	if(x==y)return x;
	for(int i=log2(dep[y]);i>=0;i--)
	{
		if(fa[y][i]!=fa[x][i])
		{
			y=fa[y][i];
			x=fa[x][i];
		}
	}
	return fa[x][0];
}//lca基本操作
int anss(int s)
{
	for(int i=head[s];i;i=edg[i].nxt)
	{
		if(edg[i].to!=fa[s][0])
		{
			anss(edg[i].to);
			d[s]+=d[edg[i].to];
		}
	}
	if(d[s]>ans)ans=d[s];
}//s及以下的子节点前缀和
int main()
{
	cin>>n>>k;
	for(int i=1;i<=n-1;i++)
	{
		int a;int b;
		cin>>a>>b;
		add(a,b);add(b,a);//双向边
	}
	dfs(1,1);
	for(int i=1;i<=k;i++)
	{
		int a;int b;
		cin>>a>>b;
		int llca=lca(a,b);
		d[a]++;d[b]++;
		d[llca]--;
		d[fa[llca][0]]--;
	}//lca--,lca的爸爸--,a、b++;
	anss(1);//这样就可以算出最大值
	cout<<ans; 
}

某个链上全部加d,求子树的前缀和

子树全部加d,求父节点的前缀和

dfs处理

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值