关于树链剖分
【懒得用手写所以直接在电脑上打了】
•用处:
Q:在一棵树上进行路径的修改、求极值、求和
如果对树上的边进行编号,然后就变成对区间的操作了。就可以利用一些数据结构(如线段树等进行操作)而如何进行编号使得用线段树处理地尽量高效,就可以利用树链剖分来解决了。【所以树链剖分一般是作为一些数据结构的预处理出现的……吧】
•有重链剖分和长链剖分两种。
长链剖分考的好像不是很多(但SCOI2017考了简直。。。)所以先暂时搁置在那,有时间再回来看【又立了一个flag】
•关于重链剖分:
•一些基础概念
重边和重儿子:
定义siz[x]为以x为根的子树节点个数,令v为u的儿子中siz值最大的节点,则v就是u的重儿子(若有多个时就任选一个),边(u,v)就是重边,其余边为轻边。
重边的性质:
(1)轻边(u,v)中,siz[v]<=siz[u/2]
(2)从根到某一点的路径上,不超过logn条轻边和不超过logn条重边
重链
重链:重边组成的链
而对于每个非重儿子,以它为根节点,又可访问出一个新的重链
这样可以证明每个点到根节点的路径上最多经过logN条重边和logN条轻边[怎么证] 所以保证了算法的高效性
看到一句话 其实树链剖分就是把边哈希到线段树上的数据结构。
【然而还是不太理解】
具体实现
//目前只写了求LCA(毕竟太弱了)
等晚上来再补题吧 flag++;
//树链剖分求LCA
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 10000+10;
int n,q;
int tot=0,head[N<<1];
struct node{
int pre,v;
}edge[N<<1];
void adde(int from,int to){
tot++;
edge[tot].pre=head[from];
edge[tot].v=to;
head[from]=tot;
}
//关于链剖部分
int fa[N],dep[N];
int son[N],siz[N];
void dfs1(int u,int f,int d){
dep[u]=d;fa[u]=f;
siz[u]=1;
for(int i=head[u];i;i=edge[i].pre){
int v=edge[i].v;
if(v==f) continue;
dfs1(v,u,d+1);
siz[u]+=siz[v];
if(son[u]==-1||siz[son[u]]<siz[v]){
son[u]=v;
}
}
}
int top[N],tag[N],seq[N];
int index=0;
void dfs2(int u,int tp){
top[u]=tp;
index++;tag[u]=index;seq[index]=u;
if(son[u]==-1) return ;
dfs2(son[u],tp);
for(int i=head[u];i;i=edge[i].pre){
int v=edge[i].v;
if(v==son[u]||v==fa[u]) continue;
dfs2(v,v);
}
}
int lca(int a, int b){
while (1){
if (top[a] == top[b]){
if(dep[a]<=dep[b]) return a;
else return b;
}
else if (dep[top[a]] >= dep[top[b]])
a = fa[top[a]];
else b = fa[top[b]];
}
}
int main(){
scanf("%d",&n);
memset(son,-1,sizeof(son));
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
adde(u,v);
adde(v,u);
}
dfs1(1,0,1);dfs2(1,1);
// for(int i=1;i<=n;i++) printf("%d ",dep[i]);
scanf("%d",&q);
while(q--){
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",lca(u,v));
}
return 0;
}