模板题:洛谷P3379
倍增(在线)
记
a
n
c
[
u
]
[
i
]
anc[u][i]
anc[u][i]表示节点
u
u
u向上跳
2
i
2^i
2i到达哪个节点,
d
e
e
p
[
u
]
deep[u]
deep[u]表示
u
u
u的深度。
每次向上让
u
,
v
u,v
u,v跳到同一深度,再向上跳到
L
C
A
LCA
LCA。
优点:容易理解,容易敲
缺点:常数大qwq
时间复杂度:预处理
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),查询每次
O
(
l
o
g
n
)
O(logn)
O(logn)。
空间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
#include<stdio.h>
#include<algorithm>
#define re register int
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=500005;
int n,m,s,cnt,head[Size];
struct Edge {
int v,next;
} w[Size<<1];
void AddEdge(int u,int v) {
w[++cnt].v=v;
w[cnt].next=head[u];
head[u]=cnt;
}
int deep[Size],anc[Size][21];
void dfs(int x,int fa) {
deep[x]=deep[fa]+1;
anc[x][0]=fa;
for(re i=1; i<=20; i++) {
anc[x][i]=anc[anc[x][i-1]][i-1];
}
for(int i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
if(nxt!=fa) {
dfs(nxt,x);
}
}
}
int LCA(int u,int v) {
if(deep[u]<deep[v]) swap(u,v);
for(re i=20; i>=0; i--) {
if(deep[anc[u][i]]>=deep[v]) {
u=anc[u][i];
}
}
if(u==v) return u;
for(re i=20; i>=0; i--) {
if(anc[u][i]!=anc[v][i]) {
u=anc[u][i];
v=anc[v][i];
}
}
return anc[u][0];
}
int main() {
n=read();
m=read();
s=read();
for(re i=1; i<n; i++) {
int u=read();
int v=read();
AddEdge(u,v);
AddEdge(v,u);
}
dfs(s,0);
while(m--) {
int u=read();
int v=read();
printf("%d\n",LCA(u,v));
}
return 0;
}
树链剖分(在线)
对于树上每个节点
u
u
u,一遍
d
f
s
dfs
dfs处理出它的子树大小
s
i
z
siz
siz,它的重儿子
s
o
n
son
son。
然后再跑一遍
d
f
s
dfs
dfs,记录每个节点的
t
o
p
top
top。
每次询问时向上不断跳到
f
a
t
h
e
r
[
t
o
p
]
father[top]
father[top]的位置即可。
优点:常数小,预处理时间复杂度低,空间复杂度低
缺点:容易出
b
u
g
bug
bug
时间复杂度:预处理
O
(
n
)
O(n)
O(n),查询每次
O
(
l
o
g
n
)
O(logn)
O(logn)
空间复杂度:
O
(
n
)
O(n)
O(n)
upd:洛谷上没卡树剖,树剖跑得还比倍增快,是之前写的树剖有误qwq
#include<stdio.h>
#include<algorithm>
#define re register int
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=500005;
int n,m,s,cnt,head[Size];
struct Edge {
int v,next;
} w[Size<<1];
void AddEdge(int u,int v) {
w[++cnt].v=v;
w[cnt].next=head[u];
head[u]=cnt;
}
int deep[Size],top[Size],father[Size],siz[Size],son[Size];
void dfs(int x,int fa) {
deep[x]=deep[fa]+1;
father[x]=fa;
siz[x]=1;
for(int i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
if(nxt!=fa) {
dfs(nxt,x);
siz[x]+=siz[nxt];
if(siz[nxt]>siz[son[x]]) {
son[x]=nxt;
}
}
}
}
void dfs2(int x,int tp) {
top[x]=tp;
if(!son[x]) return;
dfs2(son[x],tp);
for(int i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
if(nxt!=father[x] && nxt!=son[x]) {
dfs2(nxt,nxt);
}
}
}
int LCA(int u,int v) {
while(top[u]!=top[v]) {
if(deep[top[u]]>=deep[top[v]]) {
u=father[top[u]];
} else {
v=father[top[v]];
}
}
if(deep[u]<deep[v]) return u;
return v;
}
int main() {
n=read();
m=read();
s=read();
for(re i=1; i<n; i++) {
int u=read();
int v=read();
AddEdge(u,v);
AddEdge(v,u);
}
dfs(s,0);
dfs2(s,s);
while(m--) {
int u=read();
int v=read();
printf("%d\n",LCA(u,v));
}
return 0;
}
LCA转RMQ(在线)
考虑在上面的图中,在
d
f
s
dfs
dfs中遍历每条边时把起点和终点加入序列,得到序列:
1
,
5
,
1
,
2
,
4
,
2
,
3
,
2
,
1.
1,5,1,2,4,2,3,2,1.
1,5,1,2,4,2,3,2,1.
记录下每个节点最开始出现的位置
d
f
n
dfn
dfn,然后对于询问
u
,
v
u,v
u,v,转化求成
d
f
n
[
u
]
,
d
f
n
[
v
]
dfn[u],dfn[v]
dfn[u],dfn[v]之间的深度最小的节点即可。
静态最值,用
S
T
ST
ST表解决。
优点:查询复杂度低。
缺点:还没想到qwq
时间复杂度:预处理
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),查询
O
(
1
)
O(1)
O(1)
空间复杂度:
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)
注:如果想要减小常数,珂以把
l
o
g
2
log_2
log2的值都预处理出来,就不用每次调用
l
o
g
2
(
)
log2()
log2()函数。
#include<stdio.h>
#include<algorithm>
#include<math.h>
#define re register int
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=1000005;
int n,m,s,cnt,head[Size];
struct Edge {
int v,next;
} w[Size<<1];
void AddEdge(int u,int v) {
w[++cnt].v=v;
w[cnt].next=head[u];
head[u]=cnt;
}
int tot,deep[Size],dfn[Size],order[Size];
void dfs(int x,int fa) {
deep[x]=deep[fa]+1;
dfn[x]=++tot;
order[tot]=x;
for(int i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
if(nxt!=fa) {
dfs(nxt,x);
order[++tot]=x;
}
}
}
int stmin[Size][21];
int LCA(int u,int v) {
int l=dfn[u],r=dfn[v];
if(l>r) swap(l,r);
int x=log2(r-l+1);
if(deep[stmin[r][x]]<deep[stmin[l+(1<<x)-1][x]]) {
return stmin[r][x];
} else {
return stmin[l+(1<<x)-1][x];
}
}
int main() {
n=read();
m=read();
s=read();
for(re i=1; i<n; i++) {
int u=read();
int v=read();
AddEdge(u,v);
AddEdge(v,u);
}
dfs(s,0);
for(re i=1; i<=tot; i++) {
stmin[i][0]=order[i];
}
for(re j=1; j<=20; j++) {
for(re i=1<<j; i<=tot; i++) {
if(deep[stmin[i][j-1]]<deep[stmin[i-(1<<(j-1))][j-1]]) {
stmin[i][j]=stmin[i][j-1];
} else {
stmin[i][j]=stmin[i-(1<<(j-1))][j-1];
}
}
}
for(re i=1; i<=tot; i++) {
printf("%d ",order[i]);
}
while(m--) {
int u=read();
int v=read();
printf("%d\n",LCA(u,v));
}
return 0;
}
tarjan(离线)
这篇博客怎么还没写完……现在有点想睡觉……
鸽了qwq
想学的话珂以看这篇题解
优点:时空复杂度均优秀
缺点:必须离线
时间复杂度:预处理
O
(
n
)
O(n)
O(n),每次查询
O
(
1
)
O(1)
O(1)
空间复杂度:
O
(
n
)
O(n)
O(n)
#include<stdio.h>
#include<algorithm>
#include<math.h>
#define re register int
using namespace std;
typedef long long ll;
int read() {
re x=0,f=1;
char ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
x=10*x+ch-'0';
ch=getchar();
}
return x*f;
}
const int Size=500005;
int n,m,s,cnt,tot,head[Size],Qhead[Size];
int ans[Size];
struct Edge {
int id,v,next;
} w[Size<<1],Q[Size<<1];
void AddEdge(int u,int v) {
w[++cnt].v=v;
w[cnt].next=head[u];
head[u]=cnt;
}
void AddQuery(int u,int v) {
Q[++tot].v=v;
Q[tot].id=(tot+1)>>1;
Q[tot].next=Qhead[u];
Qhead[u]=tot;
}
int father[Size];
bool vis[Size];
int Find(int x) {
if(x==father[x]) return x;
return father[x]=Find(father[x]);
}
void dfs(int x,int fa) {
vis[x]=true;
father[x]=x;
for(int i=head[x]; i; i=w[i].next) {
int nxt=w[i].v;
if(nxt!=fa) {
dfs(nxt,x);
father[nxt]=x;
}
}
for(int i=Qhead[x]; i; i=Q[i].next) {
int nxt=Q[i].v;
if(vis[nxt]) {
ans[Q[i].id]=Find(nxt);
}
}
}
int main() {
n=read();
m=read();
s=read();
for(re i=1; i<n; i++) {
int u=read();
int v=read();
AddEdge(u,v);
AddEdge(v,u);
}
for(re i=1; i<=m; i++) {
int u=read();
int v=read();
AddQuery(u,v);
AddQuery(v,u);
}
dfs(s,0);
for(re i=1; i<=m; i++) {
printf("%d\n",ans[i]);
}
return 0;
}