文章目录
与重链剖分的定义相似, 只不过重儿子为链的长度最长的儿子 .
-
任意一个点 级祖先所在长链的长度一定大于等于 .
-
任意一个点跳重链到根所用的次数不超过 .
: 每次向上跳, 链的长度都会至少加 ,
对每个记录以下内容
-
它 所在长链链长 那么多次 的祖先和重儿子们, 存在
std::vector
里面, 设为 , 复杂度 . -
倍增数组 表示 的第 祖先, 复杂度 .
-
每个 的最高二进制位表示的数字 , 复杂度 .
现在求 的 级祖先, 先到达点 , 设为 ,
此时还需要跳 步, 分类讨论
- 若 然后直接跳到 即可.
- 若 然后直接跳到 即可.
设 所在长链长度为 , 则 ,
又因为 的 级及以下祖先和儿子全部存到了 中, 所以直接调用即可实现 .
#include<bits/stdc++.h>
#define pb push_back
#define reg register
const int maxn = 1000006;
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
int N;
int M;
int K;
int num0;
int o_pos;
int last_ans;
int pw[maxn];
int fa[maxn];
int len[maxn];
int son[maxn];
int dep[maxn];
int Top[maxn];
int head[maxn];
int Fk[maxn][21];
int max_pos[maxn];
std::vector <int> A[maxn], B[maxn];
struct Edge{ int nxt, to; } edge[maxn << 1];
void Add(int from, int to){
edge[++ num0] = (Edge){ head[from], to };
head[from] = num0;
}
void DFS(int k, int fa){
len[k] = 1;
dep[k] = dep[fa] + 1;
for(reg int i = 1; i <= 19; i ++) Fk[k][i] = Fk[Fk[k][i-1]][i-1];
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == fa) continue ;
Fk[to][0] = k;
DFS(to, k);
if(len[to] > len[son[k]]) son[k] = to;
}
len[k] = len[son[k]] + 1;
}
void DFS_2(int k, int top){
Top[k] = top;
if(son[k]) DFS_2(son[k], top);
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == Fk[k][0] || to == son[k]) continue ;
DFS_2(to, to);
}
}
void Work(){
o_pos = read() ^ last_ans, K = read() ^ last_ans;
if(!K){ printf("%d
", last_ans = o_pos); return ; }
else if(dep[o_pos] <= K){ printf("%d
", last_ans = 0); return ; }
int y = Fk[o_pos][max_pos[K]];
int tmp = pw[max_pos[K]];
if(K - tmp <= dep[y]-dep[Top[y]]) last_ans = B[Top[y]][dep[y]-dep[Top[y]]-(K-tmp)];
else last_ans = A[Top[y]][K-tmp - (dep[y]-dep[Top[y]])];
printf("%d
", last_ans);
}
int main(){
N = read();
for(reg int i = 1; i < N; i ++){
int x = read(), y = read();
Add(x, y), Add(y, x);
}
DFS(1, 0); DFS_2(1, 1);
pw[0] = 1;
for(reg int i = 1; i <= 19; i ++) pw[i] = pw[i-1] << 1;
for(reg int i = 1; i <= N; i ++)
for(reg int j = 19; j >= 0; j --)
if(i >= pw[j]){ max_pos[i] = j; break ; }
for(reg int i = 1; i <= N; i ++){
if(Top[i] != i) continue ;
int t = i;
for(reg int j = 1; j <= len[i]; j ++, t = Fk[t][0]) A[i].pb(t);
t = i;
for(reg int j = 1; j <= len[i]; j ++, t = son[t]) B[i].pb(t);
}
M = read();
while(M --) Work();
return 0;
}
主要是指针部分比较新, 这里说一下怎么操作,
首先有一个数组 , 其内存空间分为若干段, 分别存储了各个长链的相关信息,
在做有关 遍历到短儿子(设为)时, 在 中开一个以为开头的长链长度的空间, 来存储 往下长链的信息,
这样做的好处是: 同一长链的节点可以 通过移动指针 更新 数组 , 进而做到整体 复杂度 .
给出一棵有根树,对于每个节点,定义一个无穷序列,
其中表示以为根节点的子树中到的距离恰好为的点的个数,~,
现在对每个点,希望求出一个东西,使得对于任意 ,对于任意 .
对每个点求距离 , 使得 在使得 最大的同时 尽量小 .
设 表示距离 节点 的节点个数, ,
.
重儿子通过长链直接继承, 轻儿子暴力继承即可, 时间复杂度
#include<bits/stdc++.h>
#define reg register
const int maxn = 4e6 + 6;
int N;
int *it;
int num0;
int *F[maxn];
int Ans[maxn];
int foc[maxn];
int son[maxn];
int len[maxn];
int head[maxn];
struct Edge{ int nxt, to; } edge[maxn << 1];
void Add(int from, int to){
edge[++ num0] = (Edge){ head[from], to };
head[from] = num0;
}
void DFS(int k, int fa){
len[k] = 1;
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == fa) continue ;
DFS(to, k);
if(len[son[k]] < len[to]) son[k] = to;
}
len[k] = len[son[k]] + 1;
}
void DFS_2(int k, int fa){
F[k][0] = 1;
if(son[k]) F[son[k]] = F[k] + 1, DFS_2(son[k], k), Ans[k] = (F[son[k]][Ans[son[k]]]==1) ? Ans[k] : (Ans[son[k]]+1);
for(reg int i = head[k]; i; i = edge[i].nxt){
int to = edge[i].to;
if(to == son[k] || to == fa) continue ;
F[to] = it, it += len[to];
DFS_2(to, k);
for(reg int i = 1; i <= len[to]; i ++){
F[k][i] += F[to][i-1];
if(F[k][i] > F[k][Ans[k]] || (F[k][i] == F[k][Ans[k]] && Ans[k] > i)) Ans[k] = i;
}
}
}
int main(){
scanf("%d", &N);
for(reg int i = 1; i < N; i ++){
int x, y; scanf("%d%d", &x, &y);
Add(x, y), Add(y, x);
}
DFS(1, 0);
F[1] = it = foc;
it += len[1];
DFS_2(1, 0);
for(reg int i = 1; i <= N; i ++) printf("%d
", Ans[i]);
return 0;
}
-
- Hotel加强版
- AT2268
- AT1998
- AGC009D