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}\) 的节点个数。得:
所以,若要支持左侧两个迷你询问,我们需要支持一种查询:给定一个任意值 \(y\) ,求树上一点到根节点的所有节点中,满足 \(x+dep_x = y\) 的节点有多少个。
同理考虑右侧小询问,得:
同理,若要支持右侧两个迷你询问,我们需要支持一种查询:给定一个任意值 \(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;
}

浙公网安备 33010602011771号