hdu 6394 Tree (2018 Multi-University Training Contest 7 1009) (树分块+倍增)

链接: http://acm.hdu.edu.cn/showproblem.php?pid=6394

思路:用dfs序处理下树,在用分块,我们只需要维护当前这个点要跳出这个块需要的步数和他跳出这个块去到的下一个点的下标,这样更新和询问的复杂度就降到了sqrt(n),查询树上的点的时候我们可以用倍增来降时间复杂度,这样处理下就不会超时了,。

介绍下代码主要数组的作用方便看懂代码;
l[i] : 当前块的左边界

r[i]:当前块的右边界

num[i]: 当前点需要多少步跳出这个块

pre[i]: 这个点跳出这个块后到达的点的下标,因为是倒着跳的,pre储存的值是跳出当前块跳到上一个块到达的点的下标。

tid[i]: 当前点的dfs序

Next[i]: 因为是倒着跳的(从i点往前跳直到跳出这棵树),Next储存的是当前点下一步回跳到的点的下标。

 

实现代码:

#include
using namespace std;
const int M = 1e5 + 10;
int w[M],f[21][M],n,q,head[M],pre[M],cnt,idx,num[M],Next[M],tid[M];
int block,blo[M],l[M],r[M];
struct node{
    int to,next;
}e[M];
void init(){
    cnt = 0;
    idx = 0;
    memset(head,0,sizeof(head));
}

void add(int u,int v){
    e[++cnt].to = v;e[cnt].next = head[u];head[u] = cnt;
}

void dfs(int u,int fa){  //dfs序
    f[0][u] = fa; tid[u] = ++idx;
    for(int i = head[u];i;i = e[i].next){
        dfs(e[i].to,u);
    }
}

int find(int x,int l){  //x向上走l步会走到哪里
    for(int i = 20;i >= 0;i--){
        if((l>>i)&1)
            x = f[i][x];
    }
    return x;
}

void dfs1(int u){    //初始化
    int p = find(u,w[u]);
    Next[tid[u]] = tid[p];
    if(tid[p] < l[blo[tid[u]]]) num[tid[u]]=1,pre[tid[u]] = tid[p];
    else num[tid[u]] = num[tid[p]]+1,pre[tid[u]] = pre[tid[p]];
    for(int i = head[u];i;i=e[i].next){
        dfs1(e[i].to);
    }
}

void update(int x,int c){  //修改
    int p = find(x,c); w[x] = c;
    Next[tid[x]] = tid[p];
    if(tid[p] < l[blo[tid[x]]]) num[tid[x]] = 1,pre[tid[x]] = tid[p];
    else num[tid[x]] = num[tid[p]]+1, pre[tid[x]] = pre[tid[p]];
    for(int i = tid[x]+1;i <= r[blo[tid[x]]];i ++){
        if(Next[i] >= l[blo[i]]){
            num[i] = num[Next[i]] + 1;
            pre[i] = pre[Next[i]];
        }
    }
}

int query(int x){  //查询
    int ans = 0;
    while(x > 0){
        ans += num[x];
        x = pre[x];
    }
    return ans;
}


int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        init();
        int x;
        scanf("%d",&n);
        for(int i = 2;i <= n;i ++){
            scanf("%d",&x);
            add(x,i);
        }
        for(int i = 1;i <= n;i ++)
            scanf("%d",&w[i]);
        dfs(1,0);
        for(int i = 1;i <= 20;i ++)
            for(int j = 1;j <= n;j ++)
                f[i][j] = f[i-1][f[i-1][j]];
        block  = sqrt(n);
        for(int i = 1;i <= n;i ++)  blo[i] = (i-1)/block+1;
        for(int i = 1;i <= blo[n];i ++) l[i] = (i-1)*block+1,r[i]=i*block;
        r[blo[n]] = n;
        dfs1(1);
        scanf("%d",&q);
        while(q--){
            int op,x,y;
            scanf("%d %d",&op,&x);
            if(op == 1) printf("%d\n",query(tid[x]));
            else scanf("%d",&y),update(x,y);
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/kls123/p/9493390.html

你可能感兴趣的:(hdu 6394 Tree (2018 Multi-University Training Contest 7 1009) (树分块+倍增))