很早的时候,就有了对轻重链剖分的概念,也略微知道一些长链剖分的知识,但一直没有机会用上,所以也不算真正学习了,这次在一场比赛之中遇到了类似的题,虽然用dsu on tree的方法O(N log(N))的解决了,但是想到如果N够大的时候,卡了这个log,那么我们就需要线性O(N)的时间来解决这个问题了。
类比轻重链剖分,长链剖分的精髓就在于将原来以子树size作为评定轻重的标准,变成了子结点的最远长度,作为划分轻重链的标准,这在一些方面上降低了求解一些问题的复杂度,但是术业有专攻,它却加速了求解有关“深度问题”的时间,做到了线性。
将维护子树中只与深度有关的信息做到线性时间复杂度。——这是长链剖分的一个主要用途
于是,它就有了一些属于它的性质:
性质
- 所有链长度之和是O(N)级别的
- 任意一个点的K次祖先y所在的长(重)链的长度一定是大于等于K的
- 任意一个点向上跳跃长(重)链的次数不会超过O(sqrt(N))次
由于性质3,所以用长链剖分求LCA的复杂度就是级别的,确实不优于重链剖分。
1、2两个性质比较的好理解,性质3我们需要证明一下,
证:如果一个点从它所在的重链跳到其上的重链,那么新跳到的重链的长度一定会比它原来所在的重链的长度要长,不然为什么之前那个重链不是新重链呢?所以,最坏的重链跳法就是1,2,3,……,,为何?
是级别的,所以我们用来代替原来的N,于是就是N级别的了,最多N个点,所以最多就是跳次。
所有性质中,最重要的(或许在解决下面的问题的时候)就是性质1,这就使得我们在求解只与深度有关的信息的时候保证了复杂度。
通过举实际的例子,我们来解释这个“主要用途”。
给你一棵树,定义dx,i表示x子树内和x距离为i的节点数,对每个x求使dx,i最大的i,如有多个输出最小的。
首先,第一步要做的,是预处理的工作,得到“重儿子”。这个操作就比较的简单了,衍生长度最长的就是重儿子。
int len[maxN], Wson[maxN] = {0};
void pre_dfs(int u, int fa)
{
for(int i=head[u], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
if(v == fa) continue;
pre_dfs(v, u);
if(len[v] > len[Wson[u]]) Wson[u] = v;
}
len[u] = len[Wson[u]] + 1;
}
那么,接下去,我们怎么处理这个所谓的距离某个点深度为i的点的个数,以及知道这个dx,i呢?
这里,性质一被用上了。
所有重链的长度之和是O(N)级别的。
所以,我们只需要开一条长度为N的数组就可以处理这些信息了,然后给每个长链分配合理的数组上的空间就可以了。
所以,我开了长度为N的tmp数组,作为总的空间,然后呢,我们用指针来解决分配空间的问题。*f[maxN]指向每个点的空间的首地址,譬如说f[u],就是u点对应的首地址,它的分配长度应该是len[u],同时u必须是长链的链头。
然后具体的分配方式,以及处理方式,我们可以看一下代码:
int ans[maxN], tmp[maxN], *f[maxN], *id = tmp;
void dfs(int u, int fa)
{
f[u][0] = 1; ans[u] = 0;
if(Wson[u])
{
f[Wson[u]] = f[u] + 1;
dfs(Wson[u], u);
ans[u] = ans[Wson[u]] + 1;
}
for(int i=head[u], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
if(v == fa || v == Wson[u]) continue;
f[v] = id;
id += len[v];
dfs(v, u);
for(int j=1; j<=len[v]; j++)
{
f[u][j] += f[v][j - 1];
if((j < ans[u] && f[u][j] >= f[u][ans[u]]) || (j > ans[u] && f[u][j] > f[u][ans[u]])) ans[u] = j;
}
}
if(f[u][ans[u]] == 1) ans[u] = 0;
}
完整代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <algorithm>
#include <limits>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <bitset>
//#include <unordered_map>
//#include <unordered_set>
#define lowbit(x) ( x&(-x) )
#define pi 3.141592653589793
#define e 2.718281828459045
#define INF 0x3f3f3f3f3f3f3f3f
#define eps 1e-8
#define HalF (l + r)>>1
#define lsn rt<<1
#define rsn rt<<1|1
#define Lson lsn, l, mid
#define Rson rsn, mid+1, r
#define QL Lson, ql, qr
#define QR Rson, ql, qr
#define myself rt, l, r
#define MP(a, b) make_pair(a, b)
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uit;
typedef long long ll;
const int maxN = 1e6 + 7;
int N, head[maxN], cnt;
struct Eddge
{
int nex, to;
Eddge(int a=-1, int b=0):nex(a), to(b) {}
}edge[maxN << 1];
inline void addEddge(int u, int v)
{
edge[cnt] = Eddge(head[u], v);
head[u] = cnt++;
}
inline void _add(int u, int v) { addEddge(u, v); addEddge(v, u); }
int len[maxN], Wson[maxN] = {0};
void pre_dfs(int u, int fa)
{
for(int i=head[u], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
if(v == fa) continue;
pre_dfs(v, u);
if(len[v] > len[Wson[u]]) Wson[u] = v;
}
len[u] = len[Wson[u]] + 1;
}
int ans[maxN], tmp[maxN], *f[maxN], *id = tmp;
void dfs(int u, int fa)
{
f[u][0] = 1; ans[u] = 0;
if(Wson[u])
{
f[Wson[u]] = f[u] + 1;
dfs(Wson[u], u);
ans[u] = ans[Wson[u]] + 1;
}
for(int i=head[u], v; ~i; i=edge[i].nex)
{
v = edge[i].to;
if(v == fa || v == Wson[u]) continue;
f[v] = id;
id += len[v];
dfs(v, u);
for(int j=1; j<=len[v]; j++)
{
f[u][j] += f[v][j - 1];
if((j < ans[u] && f[u][j] >= f[u][ans[u]]) || (j > ans[u] && f[u][j] > f[u][ans[u]])) ans[u] = j;
}
}
if(f[u][ans[u]] == 1) ans[u] = 0;
}
inline void init()
{
cnt = 0;
for(int i=1; i<=N; i++) head[i] = -1;
}
int main()
{
scanf("%d", &N);
init();
for(int i=1, u, v; i<N; i++)
{
scanf("%d%d", &u, &v);
_add(u, v);
}
pre_dfs(1, 0);
f[1] = id; id += len[1];
dfs(1, 0);
for(int i=1; i<=N; i++) printf("%d\n", ans[i]);
return 0;
}