题意: 给你一颗树, 每一个节点都有一个权值, 如果一个石头落在某个节点上, 他就会往上跳这个的点的权值步。 现在有2种操作, 1 把一个石头放在 x 的位置 询问有跳几次才跳出这棵树, 2 修改某个节点的权值。
解法:树上分块, 用dfs分好块之后。 对于每一块都处理出如果石头落在某个位置之后他跳出这个块之后的位置和次数。
每次更新都自己这一块的所有子节点, 然后找第k个父亲的时候用倍增优化。
对于每次询问都跳到0号点之后,返回所经过的次数。
我们可以对属于同一块内的节点重新建立边, 因为我们在更新的时候不会直接访问到别的块的点, 所以重新建立边,避免遍历不需要的边(快了200ms)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout);
#define LL long long
#define ULL unsigned LL
#define fi first
#define se second
#define pb push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
typedef pair<int,int> pll;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = (int)1e9+;
const int N = 1e5 + ;
int n, m, b;
int head[N], to[N], nt[N]; /// E1
int head2[N], to2[N], nt2[N]; /// E2
int Stack[N], belong[N];
int jump[N], tto[N], cnt[N];
int tot, top, type, tot2;
int anc[N][];
inline void add2(int u, int v){
to2[tot2] = v;
nt2[tot2] = head2[u];
head2[u] = tot2++;
}
inline void add(int u, int v){
to[tot] = v;
nt[tot] = head[u];
head[u] = tot++;
}
void dfs(int u){
int now = top, v;
for(int i = head[u]; ~i; i = nt[i]){
v = to[i];
anc[v][] = u;
for(int i = ; i < ; i++)
anc[v][i] = anc[anc[v][i-]][i-];
dfs(v);
if(top-now >= b){
++type;
while(top!=now){
belong[Stack[top--]] = type;
}
}
}
Stack[++top] = u;
}
inline int Find(int x, int k){
for(int i = ; i >= ; i--)
if((k>>i)&) x = anc[x][i];
return x;
}
inline void Update(int x){
int z = jump[x];
if(belong[z] == belong[x]){
tto[x] = tto[z];
cnt[x] = cnt[z] + ;
}
else {
tto[x] = z;
cnt[x] = ;
}
}
void Build(int x){
Update(x);
for(int i = head[x]; ~i; i = nt[i])
Build(to[i]);
}
inline int solve(int p){
int ret = ;
while(p){
ret += cnt[p];
p = tto[p];
}
return ret;
}
void dUpdate(int x){
Update(x);
for(int i = head2[x]; ~i; i = nt2[i]){
dUpdate(to2[i]);
}
}
void dfs2(int x){
for(int i = head[x]; ~i; i = nt[i]){
if(belong[x] == belong[to[i]]){
add2(x, to[i]);
}
dfs2(to[i]);
}
}
int main(){
int t, x;
scanf("%d", &t);
while(t--){
scanf("%d", &n);
b = sqrt(n);
tot = ;top = ; type = ;tot2 = ;
memset(head, -, sizeof(int)*(n+));
memset(head2, -, sizeof(int)*(n+));
for(int i = ; i <= n; i++){
scanf("%d", &x);
add(x, i);
}
dfs();
while(top) belong[Stack[top--]] = type;
dfs2();
for(int i = ; i <= n; i++){
scanf("%d", &x);
jump[i] = Find(i, x);
}
Build();
scanf("%d", &m);
int op, k;
while(m--){
scanf("%d", &op);
if(op == ) {
scanf("%d", &x);
printf("%d\n", solve(x));
}
else {
scanf("%d%d", &x, &k);
jump[x] = Find(x, k);
dUpdate(x);
}
}
}
return ;
}
还有1种lct的写法,和弹飞绵羊的写法差不多,唯一有区别的就是找落地点在哪里,直接套lct的板子就好了,再用倍增找下一次去的位置就好了。
代码:
#include<bits/stdc++.h>
using namespace std;
#define Fopen freopen("_in.txt","r",stdin); freopen("_out.txt","w",stdout);
#define LL long long
#define ULL unsigned LL
#define fi first
#define se second
#define pb push_back
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define lch tr[x].son[0]
#define rch tr[x].son[1]
#define max3(a,b,c) max(a,max(b,c))
#define min3(a,b,c) min(a,min(b,c))
typedef pair<int,int> pll;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const LL mod = (int)1e9+;
const int N = 1e5 + ;
int n, m, b;
int head[N], to[N], nt[N];
int tot, top, type, tot2;
int anc[N][];
inline void add(int u, int v){
to[tot] = v;
nt[tot] = head[u];
head[u] = tot++;
}
void dfs(int u){
int now = top, v;
for(int i = head[u]; ~i; i = nt[i]){
v = to[i];
anc[v][] = u;
for(int i = ; i < ; i++)
anc[v][i] = anc[anc[v][i-]][i-];
dfs(v);
}
}
inline int Find(int x, int k){
for(int i = ; i >= ; i--)
if((k>>i)&) x = anc[x][i];
return x;
}
struct Node{
int son[], pre;
int sz, is_root;
inline void init() {
son[] = son[] = pre = ;
sz = is_root =;
}
}tr[N];
void Push_Up(int x){
if(!x) return ;
tr[x].sz = tr[lch].sz + tr[rch].sz + ;
}
void rotate(int x){
if(tr[x].is_root) return ;
int y = tr[x].pre, z = tr[y].pre;
int k = x == tr[y].son[];
tr[y].son[k] = tr[x].son[k^];
tr[tr[y].son[k]].pre = y;
tr[x].son[k^] = y;
tr[y].pre = x;
tr[x].pre = z;
if(tr[y].is_root) tr[x].is_root = , tr[y].is_root = ;
else tr[z].son[ tr[z].son[] == y] = x;
Push_Up(y); }
void Splay(int x){
while(!tr[x].is_root){
int y = tr[x].pre, z = tr[y].pre;
if(!tr[y].is_root){
if((y == tr[z].son[]) != ( x == tr[y].son[])) rotate(y);
else rotate(x);
}
rotate(x);
}
Push_Up(x);
}
void access(int x){
int y = ;
do{
Splay(x);
tr[rch].is_root = ;
rch = y;
tr[rch].is_root = ;
Push_Up(x);
y = x;
x = tr[x].pre;
}while(x);
}
inline void link(int u, int v){
if(v > n) v = ;
tr[u].pre = v;
}
inline void cut(int x){
access(x);
Splay(x);
tr[lch].is_root = ;
tr[lch].pre = ;
lch = ;
Push_Up(x);
}
inline int Query(int x){
access(x);
Splay(x);
return tr[lch].sz + ;
}
int main(){
int t, x;
scanf("%d", &t);
while(t--){
scanf("%d", &n);
tot = ;
memset(head, -, sizeof(int)*(n+));
tr[].init();
for(int i = ; i <= n; i++){
tr[i].init();
scanf("%d", &x);
add(x, i);
}
dfs();
for(int i = ; i <= n; i++){
scanf("%d", &x);
link(i, Find(i,x));
}
scanf("%d", &m);
int op, k;
while(m--){
scanf("%d", &op);
if(op == ) {
scanf("%d", &x);
printf("%d\n", Query(x));
}
else {
scanf("%d%d", &x, &k);
cut(x);
link(x, Find(x,k));
}
}
}
return ;
}
虽然dls说分块和lct复杂度差不多, 但是这2份代码相比较lct快了100多, 实际上我觉得树上分块比较玄学。