Tree HDU - 6394(树上分块,倍增)

题目大意

给定一个树,每个节点有个能量值代表可以往上跳的节点的个数。然后两种操作一个是查询结点跳出树的步数,一个是修改能量值

思路

可以对树进行分块。详细看代码注释

#include <map>
#include <set>
#include <cmath>
#include <vector>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

typedef long long ll;
const int maxn = 1e5 + 100;
int n, head[maxn], to[maxn], nxt[maxn], tot;
void addEdge(int u, int v) {
    to[tot]=v;nxt[tot]=head[u];head[u]=tot++;
}
int en[maxn], b, enl[maxn];
int fa[maxn][20], bel[maxn], top, st[maxn], cur;
int Next[maxn], ti[maxn];

/*分块操作*/
void dfs(int u) {
    int ltop = top;
    for(int i = head[u]; ~i; i = nxt[i]){
        int v = to[i];
        fa[v][0] = u;
        dfs(v);
        if(top - ltop >= b) {
            cur++;
            while(top > ltop) bel[st[--top]] = cur;
        }
    }
    st[top++] = u;
}

/*倍增往上跳跳跳*/
int jump(int u, int lev) {
    for(int i = 19; i >= 0; i--) {
        if((1<<i) & lev) u = fa[u][i];
    }
    return u;
}

void update(int u) {
    int s = enl[u];
    if(bel[s] != bel[u]) {  //  ti[u]就是代表着跳出这个块所需的步骤
        ti[u] = 1;
        Next[u] = s;
    }
    else {
        ti[u] = ti[s] + 1;   // 在一个块中
        Next[u] = Next[s];
    }
}

vector<int> fbel[maxn];
void dfs0(int u) {
    update(u);
    fbel[bel[u]].push_back(u);
    for(int i = head[u]; ~i;i=nxt[i]) {
        dfs0(to[i]);
    }
}

inline void init() {
    b = (int)sqrt(0.4*n);
    top=cur=0;
    dfs(1); cur++;
    while(top) bel[st[--top]] = cur;
    /*初始化倍增*/
    for(int j = 1; (1 << j) <= maxn; j++) {
        for(int i = 1; i <= n; i++) {
            fa[i][j]=fa[fa[i][j-1]][j-1];
        }
    }
    // 算出每个点可以直接跳到的点
    for(int i = 1; i <= n; i++) {
        fbel[i].clear();
        enl[i] = jump(i, en[i]);
    }
    // 自顶向下遍历
    dfs0(1);
}

inline int query(int u) {
    int ret = 0;
    while(u) {
        ret+=ti[u];
        u = Next[u];
    }
    return ret;
}
char buf[(int)2e7];int idx=0;
void read(int & x) {
    x=0;
    for(;!isdigit(buf[idx]);++idx);
    for(;isdigit(buf[idx]);++idx) x = x*10+buf[idx]-'0';
}
int main()
{
    //freopen("/Users/maoxiangsun/MyRepertory/acm/code/i.txt", "r", stdin);
    fread(buf,1,(int)2e7,stdin);
    int T;
    read(T);
    while(T--) {
        read(n);
        memset(head,-1,sizeof(int) * (n + 1)); tot=0;
        for(int i = 2; i <= n; i++) {
            int u; read(u);
            addEdge(u,i);
        }
        for(int i = 1; i <= n; i++) read(en[i]);
        init();
        int Q;
        read(Q);
        while(Q--) {
            int op;
            read(op);
            if(op == 1) {
                int u;read(u);
                printf("%d\n", query(u));
            }
            else {
                int x, y;
                read(x);read(y);
                en[x] = y;
                enl[x] = jump(x,en[x]);
                for(int i = 0; i < fbel[bel[x]].size(); i++) {
                    update(fbel[bel[x]][i]);
                }
            }
        }
    }
    return 0;
}
/*
1
4
1 2 3
1 1 1 1
3
1 4
2 3 2
1 4
*/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值