省选专练之相遇

   豪哥生活在一个n个点的树形城市里面,每一天都要走来走去。虽然走的是比较的多,但是豪哥在这个城市里面的朋友并不是很多。
    当某一天,猴哥给他展现了一下大佬风范之后,豪哥决定要获得一些交往机会来提升交往能力。豪哥现在已经物色上了一条友,打算和它(豪哥并不让吃瓜群众知道性别)交往。豪哥现在spy了一下这个人的所有行程起点和终点,豪哥打算从终点开始走到起点与其相遇。但是豪哥是想找话题的,他想知道以前有多少次行程和此次行程是有交集的,这样豪哥就可以搭上话了。这个路径与之前路径的有交集数量作为豪哥此次的交往机会。
    但是豪哥急着要做交往准备,所以算什么交往机会的小事情就交给你了。

 

好题。

主要是要发现重要性质:如果两段路径有交,则至少有一段路径的LCA在另外一段路径上。

转化为两个问题:

1)当前LCA在之前路径上  树上差分,树状数组区间修改单点查询

2)当前路径有之前LCA 树上差分,树状数组单点修改区间查询

注意封装树状数组

#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<iostream>
#pragma comment(linker,"/STACK:1024000000,1024000000")
using namespace std;
const int N=4e5+10;
struct Front_star{
	int u,v,nxt;
}e[N<<1];
int cnt=0;
int first[N];
void add(int u,int v){
	cnt++;
	e[cnt].u=u;
	e[cnt].v=v;
	e[cnt].nxt=first[u];
	first[u]=cnt;
}
int dep[N];
int fa[N][21];
int st[N];
int ed[N];
int dfn=0;
void DFS(int u,int fat){
	st[u]=++dfn;
	fa[u][0]=fat;
	for(int i=1;i<=20;++i){
		fa[u][i]=fa[fa[u][i-1]][i-1];
	}
	for(int i=first[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(v==fat)continue;
		dep[v]=dep[u]+1;
		DFS(v,u);
	}
	ed[u]=dfn;
}
int LCA(int x,int y){
	if(dep[x]<dep[y])swap(x,y);
	for(int i=20;i>=0;i--){
		if(dep[x]-(1<<i)>=dep[y])x=fa[x][i];
	}
	if(x==y)return x;
	for(int i=20;i>=0;i--){
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}
int n,m;
struct BIT{
	int T[N];
	inline int lowbit(int x){
		return x&(-x);
	}
	inline void Update(int x,int val){
		while(x<=n){
			T[x]+=val;
			x+=lowbit(x);
		}
	}
	inline int Query(int x){
		int ret=0;
		while(x){
			ret+=T[x];
			x-=lowbit(x);
		}
		return ret;
	}
}A,B;
int val[N];
int GetAns(int u,int v,int lca){
	return A.Query(st[u])+A.Query(st[v])-2*A.Query(st[lca])+B.Query(ed[lca])-B.Query(st[lca]-1)+val[lca];
}
int main(){
//	freopen("test.in","r",stdin);
	int size=40<<20;//40M	
	__asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));
	scanf("%d",&n);
	for(int i=1;i<n;++i){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	DFS(1,0);
	scanf("%d",&m);
	for(int i=1;i<=m;++i){
		int u,v;
		scanf("%d%d",&u,&v);
		int lca=LCA(u,v);
		cout<<GetAns(u,v,lca)<<'\n';
		val[lca]++;
		B.Update(st[u],1);
		B.Update(st[v],1);
		B.Update(st[lca],-2);
		A.Update(st[lca],1);
		A.Update(ed[lca]+1,-1);
	}
	exit(0);
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值