【NOIP校内模拟】相遇

【问题描述】

豪哥生活在一个 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

拿到题目的一瞬间 我就想起了之前做的一道树上差分lca来统计每个点被经过的次数(好像叫松鼠的新家来着

然而这道题好像又不一样,不能最后dfs扫一遍统计前缀和

我就YY是不是要树链剖分维护 然后怒码200行lca+树剖

成功的WA掉样例 然后就弃疗了 。。

其实正解是差不多的 发现我是少讨论了一种情况

如果两段路径有交,则其中一段路径的LCA在另外一段路径上。

然后分两种情况讨论

1)当前路径有之前的LCA

2)当前LCA在之前的路径上

对一条路径x,y,设lca=z

对于情况1

查询到x,y到根节点的lca的个数,相加起来,再减去2*z到根节点的lca的个数

修改需要给x~y区间(dfs序下)到根节点的lca的个数+1 其实就是一个单点查询+区间修改,考虑树状数组完成

 

对于情况2 求出z的子树前缀和

修改就在x,y上面打一个+1的标记,在z上面打一个-2的标记,这样就将整条链都+1了。

其实就是一个单点修改+区间查询,也是树状数组

草真是绕的一批

#include<bits/stdc++.h>
#define lowbit(x)	x&(-x)
#define N 200005
#define M 200005 
using namespace std;
struct node
{
	int to,next;
}edge[2*M];
int n,tot,first[N],m;
inline void addedge(int x,int y)
{
	tot++;
	edge[tot].to=y;
	edge[tot].next=first[x];
	first[x]=tot;
}
int up[N][22],depth[N];
int st[N],ed[N],dfn,val[N];
void dfs(int now,int fa)
{
	st[now]=++dfn;
	up[now][0]=fa;
	depth[now]=depth[fa]+1;
	for(int i=1;i<=20;i++)	up[now][i]=up[up[now][i-1]][i-1];
	for(int u=first[now];u;u=edge[u].next)
	{
		int vis=edge[u].to;
		if(vis==fa)	continue;
		dfs(vis,now);
	}
	ed[now]=dfn;
}
int lca(int x,int y)
{
	if(depth[x]<depth[y])	swap(x,y);
	//x比y深
	for(int i=20;i>=0;i--)
	{
		if(depth[up[x][i]]>=depth[y])	x=up[x][i];
	} 
	if(x==y)	return x;
	for(int i=20;i>=0;i--)
	{
		if(up[x][i]!=up[y][i])
		{
			x=up[x][i];
			y=up[y][i];
		}
	}
	return up[x][0];
}
struct BIT
{
	int T[N];
	inline void add(int x,int delta)
	{
		for(int i=x;i<=n;i+=lowbit(i))	T[i]+=delta;
	}
	inline int query(int x)
	{
		int ans=0;
		for(int i=x;i>0;i-=lowbit(i))	ans+=T[i];
		return ans;	
	}
}A,B;
inline int getans(int x,int y,int z)
{
	return A.query(st[x])+A.query(st[y])-2*A.query(st[z])+B.query(ed[z])-B.query(st[z]-1)+val[z];
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL),cout.tie(NULL);
	cin>>n;
	for(int i=1;i<=n-1;i++)
	{
		int x,y;
		cin>>x>>y;
		addedge(x,y);
		addedge(y,x);
	}
	dfs(1,0);
	cin>>m;
	for(int i=1;i<=m;i++)
	{
		int x,y,z;
		cin>>x>>y;
		z=lca(x,y);
		cout<<getans(x,y,z)<<'\n';
		val[z]++;
		
		A.add(st[z],1);
		A.add(ed[z]+1,-1);
		
		B.add(st[x],1);
		B.add(st[y],1);
		B.add(st[z],-2);
	}
	return 0;
}

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值