树上差分模板

题目描述

FJ给他的牛棚的N(2≤N≤50,000)个隔间之间安装了N-1根管道,隔间编号从1到N。所有隔间都被管道连通了。

FJ有K(1≤K≤100,000)条运输牛奶的路线,第i条路线从隔间si运输到隔间ti。一条运输路线会给它的两个端点处的隔间以及中间途径的所有隔间带来一个单位的运输压力,你需要计算压力最大的隔间的压力是多少。

输入格式
输入的第一行包含N和K。
接下来的N-1行分别包含两个整数x和y(x≠y),用于描述管道在隔间x和隔间y。
之间有一条管道。
接下来有K行,每行包含两个整数s和t来描述牛奶运输路线的起点和终点。

输出格式
输出一个整数,即隔间最大压力的值。

输入输出样例

输入 #1复制
5 10
3 4
1 5
4 2
5 4
5 4
5 4
3 5
4 3
4 3
1 3
3 5
5 4
1 5
3 4
输出 #1复制
9

题目思想

,用倍增求LCA,再将差分转移到树上,处理后遍历累积答案即可.
由于这是点差分,设从 u {u} u v {v} v, d [ ] {d[]} d[]为差分数组则有:
d s ← d s + 1 {d_s←d_s+1} dsds+1
d l c a ← d l c a − 1 {d_{lca}←d_{lca}-1} dlcadlca1
d t ← d t + 1 {d_t←d_t+1 } dtdt+1
d f ( l c a ) ← d f ( l c a ) − 1 {d_{f(lca)}←d_{f(lca)}-1} df(lca)df(lca)1
d [ u ] + + , d [ v ] + + ; {d[u]++,d[v]++;} d[u]++,d[v]++;
d [ L C A ( u , v ) ] − − , d [ f a [ L C A ( u , v ) ] ] − − {d[LCA(u,v)]--,d[fa[LCA(u,v)]]--} d[LCA(u,v)],d[fa[LCA(u,v)]].
其原理同普通差分.

参考代码

#include<bits/stdc++.h>
#define N 100005
#define re register int
#define in read()
using namespace std;

int n,m;
int poww[N],fa[N][30];
int ans=-1;
int tot=0,fi[N],nxt[2*N],to[2*N];
int lg[N],dep[N];

inline int in{
	int i=0;char ch;
	while(!isdigit(ch)){ch=getchar();}
	while(isdigit(ch)){i=(i<<3)+(i<<1)+(ch-'0');ch=getchar();}
	return i;
}//快读

inline void lian(int u,int v)//邻接表
{
	nxt[++tot]=fi[u];
	to[tot]=v;
	fi[u]=tot;
}

inline void Init(int u,int f)//LCA预处理
{
	fa[u][0]=f;
	dep[u]=dep[f]+1;
	for(int i=1;i<=lg[dep[u]];i++)
	fa[u][i]=fa[fa[u][i-1]][i-1];
	for(int i=fi[u];i;i=nxt[i])
	{
		int v=to[i];
		if(v==f)continue;
		Init(v,u);
	}
	return;
}

inline int findLCA(int x,int y)//倍增求LCA
{
	if(dep[x]<dep[y])swap(x,y);
	while(dep[x]>dep[y])
	x=fa[x][lg[dep[x]-dep[y]]-1];
	if(x==y)return x;
	for(int i=lg[dep[x]]-1;i>=0;i--)
	{
		if(fa[x][i]!=fa[y][i])
		{
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}

inline void get_ans(int u,int f)
{
	for(int i=fi[u];i;i=nxt[i])
	{
		int v=to[i];
		if(v==f)continue;
		get_ans(v,u);
		poww[u]+=poww[v];
	}
	ans=max(ans,poww[u]);
}

int main()
{
	int x,y,s,t;
	n=in,m=in;
	for(int i=1;i<=n;i++)//用于估计跳跃幅度,不加也可。
	lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	for(int i=1;i<n;i++)//连边
	{
		x=in,y=in;
		lian(x,y);
		lian(y,x);
	}
	Init(1,0);//预处理
	for(int i=1;i<=m;i++)//差分核心
	{
		s=in,t=in;
		int ant=findLCA(s,t);
		poww[s]++,poww[t]++;
		poww[ant]--,poww[fa[ant][0]]--;
	}
	get_ans(1,0);//累加求答案
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liaoxiyan123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值