学习倍增LCA很久了……却一直没有写一篇学习笔记(其实我也不太写这种东西)
对于在线LCA算法,我们先思考最暴力的方法:直接往上推,写出两个结点的祖先,然后找到最早的一个。
显而易见会T飞辣。
考虑优化一下……我们定义一个结点的深度为其到根结点的距离(当然根结点的深度就是0辣)。这样有一个好处,就是我们先可以把两个结点向上跳跃到同一高度,然后一起往上跳跃,得到的公共点就是LCA辣~
可惜这种做法不够优秀,一次查询为O(n)。我们通过多重背包的二进制优化的思想可以认识到,任何一个正数都可以被表示为二的若干次幂的和。这样的话我们跳跃的时候可以倒序枚举指数向上跳辣~这样单词查询的复杂度仅为O(logn)。
思想大约就是这样了。下面给出一份参考代码(BZOJ 3732):
/**************************************************************
Problem: 3732
User: danihao123
Language: C++
Result: Accepted
Time:340 ms
Memory:4344 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=15005,maxm=30005;
#define REP(i,n) for(i=0;i<(n);i++)
#define REP_B(i,n) for(i=1;i<=(n);i++)
#define CL_ARR(x,v) memset(x,v,sizeof(x))
#define GRAPH_REP(i,u) for(i=first[(u)];i;i=next[i])
// Graph
int first[maxn];
int next[maxm],to[maxm],dist[maxm];
int graph_cnt=0;
inline void AddEdge(int u,int v,int d){
graph_cnt++;
next[graph_cnt]=first[u];
first[u]=graph_cnt;
to[graph_cnt]=v;
dist[graph_cnt]=d;
}
// DFS
int dep[maxn];
int anc[maxn][16],maxcost[maxn][16];
void dfs(int x,int fa,int depth,int dis){
int i;
dep[x]=depth;
anc[x][0]=fa;
maxcost[x][0]=dis;
GRAPH_REP(i,x){
if(to[i]!=fa){
dfs(to[i],x,depth+1,dist[i]);
}
}
}
// Doubling LCA
int n;
inline void process(){
register int i,j,a;
dfs(1,-1,0,0);
for(j=1;(1<<j)<n;j++)
REP_B(i,n){
if(anc[i][j-1]!=-1){
a=anc[i][j-1];
anc[i][j]=anc[a][j-1];
maxcost[i][j]=max(maxcost[i][j-1],maxcost[a][j-1]);
}
}
}
int query(int x,int y){
register int i,j,log,ans=0;
if(dep[x]<dep[y])
swap(x,y);
for(log=1;(1<<log)<=dep[x];log++);
log--;
for(j=log;j>=0;j--)
if(dep[x]-(1<<j)>=dep[y]){
ans=max(ans,maxcost[x][j]);
x=anc[x][j];
}
if(x==y)
return ans;
for(j=log;j>=0;j--)
if(anc[x][j]!=-1 && anc[x][j]!=anc[y][j]){
ans=max(ans,max(maxcost[x][j],maxcost[y][j]));
x=anc[x][j];
y=anc[y][j];
}
ans=max(ans,max(maxcost[x][0],maxcost[y][0]));
return ans;
}
// Disjoint Set
int p[maxn],rank[maxn];
int find_set(int x){
if(p[x]==x)
return x;
else
return p[x]=find_set(p[x]);
}
inline void link_set(int x,int y){
if(rank[x]>rank[y]){
p[y]=x;
}else{
p[x]=y;
if(rank[x]==rank[y])
rank[y]++;
}
}
inline void union_set(int x,int y){
link_set(find_set(x),find_set(y));
}
inline bool is_same(int x,int y){
return find_set(x)==find_set(y);
}
inline void init_set(){
register int i;
REP_B(i,n)
p[i]=i;
// CL_ARR(rank,0);
}
// MST
#define EDGE_ALL(i) E[i].u,E[i].v
struct Edge{
int u,v,d;
bool operator <(const Edge& x) const{
return d<x.d;
}
};
Edge E[maxm];
int m;
void MST(){
register int i,cnt=0;
init_set();
sort(E+1,E+1+m);
REP_B(i,m){
if(!is_same(EDGE_ALL(i))){
cnt++;
union_set(EDGE_ALL(i));
AddEdge(EDGE_ALL(i),E[i].d);
AddEdge(E[i].v,E[i].u,E[i].d);
}
if(cnt==(n-1))
break;
}
process();
}
int main(){
int q,u,v;
register int i;
scanf("%d%d%d",&n,&m,&q);
REP_B(i,m){
scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].d);
}
MST();
while(q--){
scanf("%d%d",&u,&v);
printf("%d\n",query(u,v));
}
return 0;
}

1254

被折叠的 条评论
为什么被折叠?



