洛谷P8805题解
原题
思路概述
题意分析
给定一个 \(n\) 个点的无根树,每个点的权值等于其出边数量。对于给定的 \(m\) 组询问,第 \(i(1≤i≤n)\) 组询问包含两个点 \(u_i,v_i\),要求求出 \(u_i\) 到 \(v_i\) 路径上所有点的权值和。
思路简述
关注到本题数据范围 \(1≤n,m≤10^5\),每组询问必须在 \(O(\log n)\) 的时间开销内解决,想到倍增求最近公共祖先。
先通过预处理对每个点记录其祖先权值的前缀和 \(s_i\)。对于给定的两点 \(u_i,v_i\),通过倍增找到其最近公共祖先 \(LCA(u_i,v_i)\),通过三个点的权值前缀和的运算就可以得出答案。
算法实现
前缀和的运算及其推导
前缀和的转移不难推导,记录每个点连边时的入度 \(rec_i\) ,当从其祖先 \(pre\) 搜索到该点时,则有 \(s_i=s_{pre}+rec_i\)。
通过观察不难发现,对于一个点对 \(u_i,v_i\),求得其最近公共祖先 \(LCA(u_i,v_i)\) 后,可以知道其中一点 \(x(x∈\{u_i,v_i\})\) 的权值前缀和与最近公共祖先的最近前缀和之差表示了从该点到其最近公共祖先前一点所有点权和。 \(s_{u_i}+s_{v_i}-2s_{LCA(u_i,v_i)}\) 则表示了询问的两点间(包括两点)除了点 \(LCA(u_i,v_i)\) 外其他所有点权和。
于是就有:
\[ans_i=s_{u_i}+s_{v_i}-2s_{LCA(u_i,v_i)}+rec_{LCA(u_i,v_i)}
\]
AC code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#include<set>
#include<ctime>
#define RI register int
using namespace std;
const int maxn=2e5+10,maxlog=17;
typedef struct
{
int u,nex;
}side;
typedef struct
{
int pre,dep,rec,sv;
int anc[maxlog+1];
}node;
side s[maxn];
node e[maxn];
int n,m,cnt;
int fir[maxn];
inline void add(int x,int y);
inline void dfs(int x,int pre);
inline int get_LCA(int x,int y);
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin >> n >> m;
for(RI i=1,x,y;i<n;++i)
{
cin >> x >> y;
add(x,y);
}
dfs(1,0);
for(RI i=1,x,y,LCA;i<=m;++i)
{
cin >> x >> y;LCA=get_LCA(x,y);
printf("%d\n",e[x].sv+e[y].sv-((e[LCA].sv)<<1)+e[LCA].rec);
}
fclose(stdin);fclose(stdout);
return 0;
}
inline void add(int x,int y)
{
s[++cnt]=(side){y,fir[x]};fir[x]=cnt;++e[x].rec;
s[++cnt]=(side){x,fir[y]};fir[y]=cnt;++e[y].rec;
return;
}
inline void dfs(int x,int pre)
{
e[x].pre=pre;e[x].dep=e[pre].dep+1;e[x].sv=e[x].rec+e[pre].sv;e[x].anc[0]=pre;
for(RI i=1;i<=maxlog;++i) e[x].anc[i]=e[e[x].anc[i-1]].anc[i-1];
for(RI i=fir[x];i;i=s[i].nex)
if(s[i].u!=pre) dfs(s[i].u,x);
return;
}
inline int get_LCA(int x,int y)
{
if(e[x].dep<e[y].dep) swap(x,y);
for(RI i=maxlog;i>=0;--i) if(e[e[x].anc[i]].dep>=e[y].dep) x=e[x].anc[i];
if(x==y) return x;
else
{
for(RI i=maxlog;i>=0;--i)
if(e[x].anc[i]!=e[y].anc[i])
{
x=e[x].anc[i];
y=e[y].anc[i];
}
return e[x].anc[0];
}
}
浙公网安备 33010602011771号