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

题目链接: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);
}

猜你喜欢

转载自blog.csdn.net/qq_35320178/article/details/81942124