G
N
I
D
A
O
L

T3: C. 树上询问(query)

题目链接

题意不说了

part1:转化

首先dfs求出每个点的dep并预处理倍增lca。

对于一个询问,我们可以求出 \(l,r\) 把它拆成两个 \((lca,l]\) , \([lca,r]\) 小询问。然后可以利用差分思想,拆成四个 \([1,l]\) , \([1,lca]\) , \([1,lca)\) , \([1,r]\) 四个迷你询问。

以防你看不懂括号是什么意思:\((x,y]\) 表示从 \(x\)\(y\) 但不包含 \(x\)\([x,y)\) 表示从 \(x\)\(y\) 但不包含 \(y\)\([x,y]\) 表示从 \(x\)\(y\) 且既包含 \(x\) 也包含 \(y\)

拆询问过程如图所示:

无标题

由四个迷你询问的答案做加减就可以算出原询问的答案。拆出来的迷你询问在问什么?

我们考虑左侧小询问。我们设第 \(x\) 个节点深度为 \(dep_x\) ,第 \(x\) 个节点与第 \(y\) 个节点的距离为 \(dis_{x,y}\)

左侧小询问的本质为求链 \((lca,l]\) 中,\(x=dis_{x,l}\) 的节点个数。得:

\[\begin{align*} x&=dis_{x,l} \\ x&=dep_l-dep_x \\ x+dep_x&=dep_l \\ \end{align*}\]

所以,若要支持左侧两个迷你询问,我们需要支持一种查询:给定一个任意值 \(y\) ,求树上一点到根节点的所有节点中,满足 \(x+dep_x = y\) 的节点有多少个。

同理考虑右侧小询问,得:

\[\begin{align*} x&=dis_{x,l} \\ x&=dis_{lca,l}+dis_{lca,x} \\ x&=(dep_l-dep_{lca})+(dep_x-dep_{lca})) \\ x-dep_x&=dep_l-2 \times dep_{lca} \\ \end{align*}\]

同理,若要支持右侧两个迷你询问,我们需要支持一种查询:给定一个任意值 \(y\) ,求树上一点到根节点的所有节点中,满足 \(x-dep_x = y\) 的节点有多少个。(左,右侧式子中 \(x\)\(dep_x\) 间的符号不同)

part2:离线处理

我们定义数组 \(ans\) , \(ans_i\) 表示第 \(i\) 个大询问的答案。

对于每个迷你询问,我们定义一个结构体存储。

struct qes{
	int id,y,op,gx;
	//id:该迷你询问由哪个大询问拆得
	//y:迷你询问条件"给定一个常数y"中给定的常数y
	//op:该迷你询问需要求“dep[x]+x"还是 “dep[x]-x"
	//gx:该迷你询问对大询问做正贡献还是负贡献 
};

在每个节点开一个vector,用于存储区间为“根到该节点”的迷你询问。

定义两个数组:\(s1\)[2000000] , \(s2\)[2000000]。 \(s1\) 用于求解“dep[x]+x"类询问的答案,\(s2\) 用于求解“dep[x]-x"类询问的答案。

我们从1号节点开始dfs,对于每个节点分4步操作:

1.用该节点信息更新s1,s2数组

2.遍历当前节点的vector,求解当前节点上的所有迷你询问

3.递归搜索子节点

4.撤销当前节点对s1,s2数组的更新,回溯。

具体的,当搜索到一个节点x时,令s1[x+dep[x]]++; s2[x-dep[x]]++;(即1操作)

从节点x回溯时,令s1[x+dep[x]]--; s2[x-dep[x]]--;(即4操作)

这样当搜索到任何节点后,s1[i]就表示从1到当前节点所有满足 x+dep[x]==i 的节点个数,s2[i]就表示从1到当前节点所有满足 x-dep[x]==i 的节点个数。

我们在搜索到一个节点后遍历这个节点的vector,若遍历到的询问 op==1 ,则 ans[id]+=gx*(s1[y]); ,否则 ans[id]+=gx*(s2[y]); (操作2)。

这样我们就可以利用一次dfs求出所有迷你询问的答案并将其贡献到对应的大询问。

最后,我们遍历ans数组并输出即可。

一点细节:数组下标不能为负,所以访问和更新s1,s2时令下标+1000000

part3:代码

代码流程:dfs求dep,并预处理倍增lca --> 拆询问并挂到对应节点 --> dfs求解答案

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=(x<<1)+(x<<3)+(ch-'0');
		ch=getchar();
	}
	return x*f;
}
inline void write(int x)
{
	if(x<0)x*=-1,putchar('-');
	if(x>9)write(x/10);
	putchar(x%10+'0');
	return;
}
int n,m;
int ans[300005];
struct qes{
	int id,x,op,gx;
	qes(int x1,int x2,int x3,int x4){
		id=x1;x=x2;op=x3;gx=x4;
	}
}; 
vector<qes> q[300005];
struct Tree{
	int h[300005],to[600005],nxt[600005],tot;
	void add(int x,int y){
		tot++;
		to[tot]=y;
		nxt[tot]=h[x];
		h[x]=tot;
	}
	int fa[300005][25];
	int dep[300005];
	void dfs1(int x){
		int y=h[x];
		while(y){
			int t=to[y];
			y=nxt[y];
			if(t==fa[x][0]){
				continue;
			}
			fa[t][0]=x;
			dep[t]=dep[x]+1;
			for(int i=1;i<=20;i++){
				fa[t][i]=fa[fa[t][i-1]][i-1];
			}
			dfs1(t);
		}
	}
	int lca(int x,int y){
		if(dep[x]>dep[y]){
			swap(x,y);
		}
		for(int i=20;i>=0;i--){
			if(dep[fa[y][i]]>=dep[x]){
				y=fa[y][i];
			}
		}
		if(x==y){
			return x;
		}
		for(int i=20;i>=0;i--){
			if(fa[y][i]!=fa[x][i]){
				y=fa[y][i];x=fa[x][i];
			}
		}
		if(x==y){
			return x;
		}return fa[x][0];
	}
	int s1[2000006];
	int s2[2000006];
	void dfs(int x){
		s1[x+dep[x]+1000000]++;
		s2[x-dep[x]+1000000]++;
		for(int i=0;i<q[x].size();i++){
			if(q[x][i].op==1){
				ans[q[x][i].id]+=q[x][i].gx*(s1[q[x][i].x+1000000]);
			}else{
				ans[q[x][i].id]+=q[x][i].gx*(s2[q[x][i].x+1000000]);
			}
		}
		int y=h[x];
		while(y){
			int t=to[y];
			y=nxt[y];
			if(t==fa[x][0]){
				continue;
			}
			dfs(t);
		}
		s1[x+dep[x]+1000000]--;
		s2[x-dep[x]+1000000]--;
	}
};Tree t;
signed main()
{
	freopen("query.in","r",stdin);
	freopen("query.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<n;i++){
		int x=read();int y=read();
		t.add(x,y);t.add(y,x);
	}
	t.dep[1]=1;
	t.dfs1(1);
	for(int i=1;i<=m;i++){
		int x=read();int y=read();
		if(x==y)continue;
		int l=t.lca(x,y);
		q[x].push_back(qes(i,t.dep[x],1,1));
		q[y].push_back(qes(i,t.dep[x]-2*t.dep[l],-1,1));
		q[l].push_back(qes(i,t.dep[x],1,-1));
		q[t.fa[l][0]].push_back(qes(i,t.dep[x]-2*t.dep[l],-1,-1));
	}
	t.dfs(1);
	for(int i=1;i<=m;i++){
		write(ans[i]);putchar('\n');
	}
	return 0;
}

posted @ 2026-06-02 14:28  MagicalGirl_MH  阅读(56)  评论(8)    收藏  举报