相遇[dfs序][lca]

豪哥生活在一个n个点的树形城市里面,每一天都要走来走去。虽然走的是比较的多,但是豪哥在这个城市里面的朋友并不是很多。

当某一天,猴哥给他展现了一下大佬风范之后,豪哥决定要获得一些交往机会来提升交往能力。豪哥现在已经物色上了一条友,打算和它(豪哥并不让吃瓜群众知道性别)交往。豪哥现在spy了一下这个人的所有行程起点和终点,豪哥打算从终点开始走到起点与其相遇。但是豪哥是想找话题的,他想知道以前有多少次行程和此次行程是有交集的,这样豪哥就可以搭上话了。这个路径与之前路径的有交集数量作为豪哥此次的交往机会。

但是豪哥急着要做交往准备,所以算什么交往机会的小事情就交给你了。

输入

第一行一个正整数n表示节点个数。接下来n-1行,每行两个正整数分别是u,v表示节点u和v之间有连边。接下来一行一个 正整数m表示路径个数。然后有m行,每行两个正整数分别是u,v分别表示u到v之间有一条路径

输出

输出共m行,每行一个整数,第i行表示豪哥在这条路径上获得的交往机会。

样例输入

5
1 2
1 3
3 4
3 5
4
4 5
4 2
1 3
1 2

样例输出

0
1
2
2

【数据范围与约定】

对于20%的数据n,m≤2000

对于另外20%的数据n,m≤50000

对于另外10%的数据n,m≤200000保证树形结构是一条链

对于另外50%的数据n,m≤200000


题意:求当前路径与之前多少条路径相交

分析:

[知识点1]: 路径(a,b) 与 路径(c,d) 相交

当且仅当 lca(a,b)在路径(c,d)上  或   lca(c,d)在路径(a,b)上

 

[知识点2]: dfs序维护 "单点修改, 路径查询""路径修改 , 单点查询"

也就是说,新来一条路径,我们需要查询之前lca落在这条路径上的个数  和   这个lca落在其它路径上的个数

第一个即为 "单点修改, 路径查询"

第二个也就成为 "路径修改 , 单点查询"

那不是我们熟悉的dfs序维护吗

 

对于"单点修改, 路径查询"

我们修改点时修改整棵子树 因为x的修改只对x的子树有贡献

查询分成3个  即 quary(x)+quary(y) - 2*quary(lca)

对于"路径修改 , 单点查询"

我们单点修改3个值(同上)

然后点的值即为它的子树和  因为子树的路径对它有贡献


巨佬cxr_o__o_cxr 的代码
#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int x=0;char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x;
}
struct edge{
	int u,v,nxt;
}e[800010];
int first[400010],cnt=0;
inline void add(int u,int v){
	e[++cnt].u=u;e[cnt].v=v;
	e[cnt].nxt=first[u];first[u]=cnt;
}
int n,m;
struct BIT{
	int c[800010]={0};
	inline void add(int x,int w){//x&-x   —>   x的二进制最右边的一个一的值 
		for(;x<=n;x+=x&-x)c[x]+=w;
	}
	inline int ask(int x){
		int an=0;
		for(;x;x-=x&-x)an+=c[x];
		return an;
	}
}T1,T2;
//T1区间修改单点查询      T2单点修改区间查询
//T1别的路径的LCA在当前路径上(包括两个LCA相重)
//T2当前路径的LCA在之前的路径上(不包括两个LCA相重) 
int fa[400010][20],dep[400010],st[400010],ed[400010],tim=0;
void dfs(int x,int f){
	st[x]=++tim;//dfs序 
	for(int i=1;(1<<i)<=dep[x];i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=first[x];i;i=e[i].nxt){
		int y=e[i].v;
		if(y==f)continue;
		fa[y][0]=x;
		dep[y]=dep[x]+1;
		dfs(y,x);
	}
	ed[x]=tim;
}
int lca(int a,int b){
	if(dep[a]<dep[b])swap(a,b);
	int t=dep[a]-dep[b];
	for(int i=0;(1<<i)<=t;i++)
		if(t&(1<<i))a=fa[a][i];//t的二进制在i这一位上为1
	if(a==b)return a;//
	for(int i=19;i>=0;--i)
		if(fa[a][i]!=fa[b][i])
			a=fa[a][i],b=fa[b][i];
	return fa[a][0];
}
int val[400010];
int ans(int u,int v,int c){
	int e=T1.ask(st[u])+T1.ask(st[v])-2*T1.ask(st[c])+val[c];
	//cout<<c<<" "<<st[c]<<" "<<ed[c];
	int f=T2.ask(ed[c])-T2.ask(st[c]-1);
	return e+f;
}
int main(){
	n=read();
	for(int i=1;i<n;i++){
		int u,v;
		u=read();v=read();
		add(u,v);add(v,u);
	}
	dep[1]=1;
	dfs(1,0);
	m=read();
	while(m--){
		int u,v,c;
		u=read();v=read();
		c=lca(u,v);
		//cout<<c<<" ";
		printf("%d\n",ans(u,v,c));
		val[c]++;//特判LCA
		T2.add(st[u],1);
		T2.add(st[v],1);
		T2.add(st[c],-2);
		T1.add(st[c],1);
		T1.add(ed[c]+1,-1);
	}
	exit(0);//必须用exit 
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

FSYo

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

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

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

打赏作者

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

抵扣说明:

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

余额充值