高等理论计算机科学 LCA+树上差分(树上的相交路径条数)Hihocoder-1167

本文介绍了一种利用树上差分解决特定路径交叉问题的方法,并详细展示了如何通过跳重链求解最低公共祖先(LCA)。适用于解决多条路径相交问题,特别是当一条路径的LCA位于另一条路径上时。

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

题目链接:Hihocoder-1167

主要思路:

多画几棵树可以看出两条路径相交时只有一条路径的LCA落在另一条路径上,故你可以求每个LCA落在几条路径上(若两条路径LCA相同,这时候就会算两遍,故将LCA从路径上剔除,特判即可)。

求每个LCA落在几条路径上就可以用树上差分。

AC代码:

#include<cstdio>
#include<algorithm>
#include<vector>
#define lowbit(x) x&-x
#define M 100005
using namespace std;
struct E {
	int to,nx;
} edge[M<<1];
int tot,head[M];
void Addedge(int a,int b) {
	edge[++tot].to=b;
	edge[tot].nx=head[a];
	head[a]=tot;
}
struct Path {
	int from,to;
	bool operator <(const Path &x)const {
		return to<x.to;
	}
} way[M];
struct p100 { //若两条路径相交则必有一条的LCA落在另一条上
	int n,m;
	int sz[M],fa[M],son[M],dep[M];
	void dfs(int now) {
		sz[now]=1;
		son[now]=0;
		for(int i=head[now]; i; i=edge[i].nx) {
			int nxt=edge[i].to;
			if(nxt==fa[now])continue;
			fa[nxt]=now;
			dep[nxt]=dep[now]+1;
			dfs(nxt);
			if(sz[son[now]]<sz[nxt])son[now]=nxt;
			sz[now]+=sz[nxt];
		}
	}
	int top[M];
	void redfs(int now) {
		if(son[now]) {
			int nxt=son[now];
			top[nxt]=top[now];
			redfs(nxt);
		}
		for(int i=head[now]; i; i=edge[i].nx) {
			int nxt=edge[i].to;
			if(nxt==fa[now]||nxt==son[now])continue;
			top[nxt]=nxt;
			redfs(nxt);
		}
	}
	int LCA(int a,int b) {//跳重链求LCA 
		while(top[a]!=top[b]) {
			if(dep[top[a]]<dep[top[b]])swap(a,b);
			a=fa[top[a]];
		}
		return dep[a]<dep[b]?a:b;
	}
	long long ans;
	void ansdfs(int now) {
		for(int i=head[now]; i; i=edge[i].nx) {
			int nxt=edge[i].to;
			if(nxt==fa[now])continue;
			ansdfs(nxt);
			sum[now]+=sum[nxt];//sum即为当前点在几条线段上 
		}
		ans+=1ll*sum[now]*cnt[now]+1ll*cnt[now]*(cnt[now]-1ll)/2;//两个LCA相同会多算出情况,故特判
	}
	int sum[M],cnt[M];
	void solve(int n,int m) {
		ans=0;
		this->n=n;
		this->m=m;
		dfs(1);
		top[1]=1;
		redfs(1);
		for(int i=1; i<=m; i++) {
			sum[way[i].from]++;//差分 
			sum[way[i].to]++;
			int x=LCA(way[i].from,way[i].to);
			sum[x]-=2;//不把LCA算上去 
			cnt[x]++;
		}
		ansdfs(1);
		printf("%lld\n",ans);
	}
} P100;
int main() {
	int n,m;
	scanf("%d%d",&n,&m);
	for(int i=1; i<n; i++) {
		int a,b;
		scanf("%d%d",&a,&b);
		Addedge(a,b);
		Addedge(b,a);//***建反向边 
	}
	for(int i=1; i<=m; i++) {
		scanf("%d%d",&way[i].from,&way[i].to);
		if(way[i].from>way[i].to)swap(way[i].from,way[i].to);
	}
	P100.solve(n,m);
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值