hdu 6394 分块大法好 lct

本文介绍了一个树形结构上的跳跃问题,通过分块技术优化查询效率,实现快速计算从指定节点跳出整棵树所需的最少跳跃次数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Tree
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 446 Accepted Submission(s): 158

Problem Description
Alice and Bob are playing with a magic tree This magic tree has n nodes,with n−1 magic paths connecting them into a connected block. Node 1 is at the top of the magic tree (layer 0). Node i is at the kth layer, where k is the distance from the node i to the node 1. Alice and Bob give a mana value on each node. If a magic stone falls on node i, it will be sent up to the k layer and appear on the kth ancestor node of the i layer(k is the mana value of node i). This node will continue to send up it, and so on. If the layer of node i is less than k, this stone will be sent out of the magic tree. Alice is curious, she will modify the magic value of a node, and ask Bob: If you drop a magic stone on the node x, how many times does it take to transfer it out of the magic tree?

Input
Input contains multiple tests
The first line contains one integer T(T≤4), indicating the number of test cases.
The following lines describe all the test cases
For each test case: The first line contains an integer n(n≤100000), indicating the size of the magic tree.
The second line has n−1 numbers, and the ith number represents the father of the node i+1.
The third row has n numbers, and the ith number represents the initial mana ai(ai≤n) value of each node.
In the fourth line, a number m(m≤100000) represents the number of operations.
The next m lines, one operation per line.
First a number op(1≤op≤2) represents the type of operation.
If op==1, a number x will be read immediately, indicating that a magic stone is thrown to the node x.
If op==2, it will immediately read in two numbers x and new_a, indicating that the magic value of node x is modified to new_a(new_a≤n).

Output
For each query with op==1, output the answer

Sample Input
1
4
1 2 3
1 1 1 1
3
1 4
2 3 2
1 4

Sample Output
4
3

Hint

For the first query: 4->3->2->1->out
For the second query:4->3->1->out

题意:给出一棵树,然后每个节点有一个权值,代表这个点可以往上面跳多远,问最少需要多少次可以跳出这颗树,
做法:分块,对于每一块这样维护,每个点维护两个值a[i],b[i],a[i]代表这个点需要跳多少次可以跳出这块,即跳到前面那一块去,b[i]代表这个点跳出这一块的时候会跳到那个点去,这样询问的时候就可以最多跳sqrt(n)次就以跳出去了,
更新的话,每次把它所在的那一块全部更新就好了,
思考:分块不仅可以处理区间查询问题,它像是一种路径压缩,没隔sqrt(n)做一个标记,然后遍历就可以在sqrt(n)种完成了,更新的话只需要更新它影响的点,,,emmm,好神奇的样子,
再说一个分块的思想:维护可持久化并查集,也可以用分块来实现,做法就是每隔sqrt(n)就把这个并查集的状态存下来,然后你想得到的并查集就可以在nsqrt(n),的时间复杂度和空间复杂度下实现了。是不是跟这个很相似,没有办法压缩的路径,就暴力sqrt(n)维护。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+337;
int fa[N],a[N],b[N];//a : 需要多少步可以跳到下一个块,b:跳到下一块的哪一个点
int lg[N];
int st[N][20];
int B = 333;
int get(int x,int k){
    for(int i = 0;(1<<i) <= k; i ++){
        if((1<<i)&k) x = st[x][i];
    }
    return x;
}

void update(int x){
    int t = x*B;
    if(t == 0){
        a[0] = 0;b[0] = 0;
        t = 1;
    }
    for(int i = t;i < x*B+B;i ++){
        if(fa[i] < x*B) {
            a[i] = 1;
            b[i] = fa[i];
        }
        else{
            a[i] = a[fa[i]]+1;
            b[i] = b[fa[i]];
        }
       // if(i < 10) cout << i << ' '<<a[i] << ' '<<b[i] << ' '<<fa[i] << endl;
    }
}

int query(int x){
    int cnt = 0;
    while(x){
        cnt += a[x];
        x = b[x];
    }
    return cnt;
}

int main(){
    int now = 2;
    lg[1] = 0;
    for(int i = 1;i < N;i ++){
        lg[i] = lg[i-1];
        if(now < i){
            now *= 2;
            lg[i]++;
        }
    }
    int T;
    cin >> T;
    while(T--){
        int n;
        scanf("%d",&n);
        memset(st,0,sizeof(st));
        for(int i = 2;i <= n;i ++){
            scanf("%d",&st[i][0]);
        }
        for(int i = 1;i <= lg[n];i ++){
            for(int j = 1;j <= n;j ++){
                st[j][i] = st[st[j][i-1]][i-1];
            }
        }

        for(int i = 1;i <= n;i ++){
            int now;
            scanf("%d",&now);
            fa[i] = get(i,now);
        }
        for(int i = 0;i <= n/B;i ++){
            update(i);
        }
        int q;
        scanf("%d",&q);
        for(int i = 1;i <= q;i ++){
            int op,x,y;
            scanf("%d",&op);
            if(op == 1){
                scanf("%d",&x);
                printf("%d\n",query(x));
            }
            else{
                scanf("%d %d",&x,&y);
                fa[x] = get(x,y);
                update(x/B);
            }
        }
    }
    return 0;
}

lct:

#include<cstdio>
#include<algorithm>
#include<iostream>
using namespace std;


inline int read(){
    int xx=0,f=1;char ch=getchar();
    for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
    for(;ch>='0'&&ch<='9';ch=getchar())xx=xx*10+ch-'0';
    return xx*f;
}

const int maxn=200010;
int fa[maxn][20];
struct node{
    int fa,ch[2],size;
    bool is_root;
}T[maxn];
int n,m,k[maxn];

void update(int x){
    T[x].size=1;
    if(T[x].ch[0])T[x].size+=T[T[x].ch[0]].size;
    if(T[x].ch[1])T[x].size+=T[T[x].ch[1]].size;
}
int getson(int x){
    return x==T[T[x].fa].ch[1];
}
void rotate(int x){
    if(T[x].is_root)return;
    int k=getson(x),fa=T[x].fa;
    int fafa=T[fa].fa;
    T[fa].ch[k]=T[x].ch[k^1];
    if(T[x].ch[k^1])T[T[x].ch[k^1]].fa=fa;
    T[x].ch[k^1]=fa;
    T[fa].fa=x;
    T[x].fa=fafa;
    if(!T[fa].is_root)T[fafa].ch[fa==T[fafa].ch[1]]=x;
    else T[x].is_root=true,T[fa].is_root=false;
    update(fa);update(x);
}
void Splay(int x){
    for(int fa;!T[x].is_root;rotate(x)){
        if(!T[fa=T[x].fa].is_root){
            rotate((getson(x)==getson(fa))?fa:x);
        }
    }
}
void access(int x){
    int y=0;
    do{
        //cout <<x << ' '<<y << endl;
        Splay(x);
        T[T[x].ch[1]].is_root=true;
        T[T[x].ch[1]=y].is_root=false;
        update(x);
        x=T[y=x].fa;
    }while(x);
}
void link(int u,int v){
    if(v>n)v=0;
    access(u);
    Splay(u);
    T[T[u].ch[0]].is_root=true;
    T[T[u].ch[0]].fa=0;
    T[u].ch[0]=0;
    T[u].fa=v;update(u);
}
int Query(int x){

    access(x);
    //cout<<"!!!" <<x << endl;
    Splay(x);
    return T[T[x].ch[0]].size+1;
}
int get(int x,int y){
    for(int i = 0;(1<<i)<=y;i ++){
        if((1<<i)&y) x = fa[x][i];
    }
    return x;
}
void Work(){
    int opt,x,y;
    while(m--){
        //opt=read();x=read();
        scanf("%d %d",&opt,&x);
        if(opt==1)printf("%d\n",Query(x));
        else if(opt==2){
            //y=read();
            scanf("%d",&y);
            link(x,get(x,y));
        }
    }
}

int main(){
    //n=read();
    int Ts;
    scanf("%d",&Ts);
    while(Ts--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            T[i].is_root=T[i].size=1;
            T[i].fa=T[i].ch[0]=T[i].ch[1]=0;
        }
        fa[1][0] =0;
        for(int i = 2;i <= n;i ++){
            int now;
            scanf("%d",&now);
            fa[i][0] = now;
        }
        for(int i = 1;i < 20;i ++){
            for(int j = 1;j <= n;j ++){
                fa[j][i] = fa[fa[j][i-1]][i-1];
            }
        }

        for(int i=1;i<=n;i++){
            //k[i]=read();
            scanf("%d",&k[i]);
            //cout << "!!" << i << ' '<<' '<<k[i] << ' '<< get(i,k[i])<<endl;
            link(i,get(i,k[i]));
        }
        scanf("%d",&m);
        Work();
    }


    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值