1. 程式人生 > >HDU 6394 Tree 樹分塊或者LCT 2018杭電多校第七場

HDU 6394 Tree 樹分塊或者LCT 2018杭電多校第七場

Tree

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 380    Accepted Submission(s): 130

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

題意:題意很明確, 就是給出一棵有根樹, 然後每個點都有一個權值, 表示這個點可以傳送到它的第a[i]個父親節點上, 然後給出q個查詢,每個查詢有兩種表示形式, 第一種:詢問在x點經過幾次傳送可以傳送出樹(向上傳送出根則表明傳送出樹) 第二種:將x點的權值更新為y

思路:有兩種思路, 第一種是樹分塊, 第二種是LCT鑑於弱雞不會LCT。。就只能說一下樹分塊的思路, 首先將樹根據dfs序分塊, 然後記錄每個節點向上跳到下一個塊所需要的次數, 然後再記錄跳到下一個塊中的那個點是多少, 對於第一種查詢, 我們只需要按塊暴力就行, 對於第二種修改, 我們也只需要修改與x處在同一個塊裡面, 且dfs序在x以下的點就可以了, 這樣我們每次的複雜度都是塊的大小, 或者塊的個數, 這樣複雜度基本可以過。 這裡找父親節點的時候要用倍增優化以下, 而且注意! 這裡的塊不要開根號n, 直接固定開100的大小就行了, 因為根號下1e5差不多是350, 這樣的話如果每次都要修改或者查詢的話是吃不消的, 不如直接開成100, 這樣修改最大是100, 查詢最大時1000, 這樣複雜度怎麼算都是差不多的。

程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>

const int maxed = 100000 + 10;

struct E
{
    int v, bef;
}e[maxed];

int n, m, ans, head[maxed], p[maxed];
int C[18], fa[maxed][18];
int block_size, block, size_[maxed], belong[maxed], cnt[maxed], id[maxed];

int main()
{
    void add_(int x, int y);
    void slove_1(int u);
    void slove_2(int u, int cur);
    for (int i = 0; i < 18; ++i)
        C[i] = (1 << i);
    int N;
    scanf("%d", &N);
    while (N--) {
        ans = 1;
        memset(head, -1, sizeof(head));
        scanf("%d", &n);
        int a;
        for (int i = 1; i <= n - 1; ++i) {
            scanf("%d", &a);
            add_(a, i + 1);
        }
        for (int i = 1; i <= n; ++i)
            scanf("%d", &p[i]);
        block_size = 100;
        memset(size_, 0, sizeof(size_));
        block = 1;
        belong[1] = 1, size_[1] = 1;
        for (int i = 0; i < 18; ++i)
            fa[1][0] = 0;
        slove_1(1);
        int b, c;
        scanf("%d", &m);
        while (m--) {
            scanf("%d%d", &a, &b);
            if (a == 1) {
                int ans = 0, now = b;
                while (true) {
                    ans += cnt[now];
                    now = id[now];
                    if (!now)
                        break;
                }
                printf("%d\n", ans);
            }
            else {
                scanf("%d", &c);
                p[b] = c;
                slove_2(b, b);
            }
        }
    }
    return 0;
}

void add_(int x, int y)
{
    e[ans].v = y;
    e[ans].bef = head[x];
    head[x] = ans++;
}

void slove_1(int u)
{
    cnt[u] = 1, id[u] = 0;
    int wa = u, w = p[u];
    while (true) {
        for (int i = 17; i >= 0; --i)
            if (C[i] <= w) {
                w -= C[i];
                wa = fa[wa][i];
                break;
            }
        if (!wa)
            break;
        if (!w) {
            if (belong[u] == belong[wa]) {
                //std::cout << u << "=====" << wa << "====" << cnt[wa] << std::endl;
                cnt[u] += cnt[wa];
                id[u] = id[wa];
            }
            else
                id[u] = wa;
            break;
        }
    }
    for (int i = head[u]; i != -1; i = e[i].bef) {
        int v = e[i].v;
        if (size_[belong[u]] < block_size) {
            size_[belong[u]] += 1;
            belong[v] = belong[u];
        }
        else {
            belong[v] = ++block;
            size_[belong[v]] += 1;
        }
        fa[v][0] = u;
        for (int j = 1; j < 18; ++j)
            fa[v][j] = fa[fa[v][j - 1]][j - 1];
        slove_1(v);
    }
}

void slove_2(int u, int cur)
{
    cnt[u] = 1, id[u] = 0;
    int wa = u, w = p[u];
    while (true) {
        for (int i = 17; i >= 0; --i)
            if (C[i] <= w) {
                w -= C[i];
                wa = fa[wa][i];
                break;
            }
        if (!wa)
            break;
        if (!w) {
            if (belong[u] == belong[wa]) {
                cnt[u] += cnt[wa];
                id[u] = id[wa];
            }
            else
                id[u] = wa;
            break;
        }
    }
    for (int i = head[u]; i != -1; i = e[i].bef) {
        int v = e[i].v;
        if (belong[v] != belong[cur])
            continue;
        slove_2(v, cur);
    }
}