1 条题解

  • 0
    @ 2025-8-24 21:34:24

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Parabola
    mua~mua~嘟嘟你的嘴

    搬运于2025-08-24 21:34:24,当前版本为作者最后更新于2019-02-15 15:08:13,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    Part0.题外话

    总算搞出来这一道题了

    从上午8:30做到下午2:30

    饭都没吃

    累死我了

    不过想到明天能去买轻小说就敲开心

    这篇题解是一边做饭一边写出来的

    另外,窝的做法是目前(2019/2/15)所有题解内中最优复杂度

    不知道还有没有神仙能够更快

    Part1.前置知识

    分块、分块、还有分块

    好吧其实还有一个并查集

    Part2.BB了那么久总算开始了

    首先这一题的所谓「树分块」的做法是错误的,可以被菊花图卡掉

    所以要换种方法分块

    回答一个询问,就是把初始答案与每一个操作带来的影响所整合起来

    到这一题上,假设窝们对于初始那棵树(没执行过任何一次修改操作的)进行一次dfs算出dfs序后

    树上问题就成了序列问题

    那么以uu为根的子树所对应的区间就是[dfn[u],dfn[u]+sz[u]1][dfn[u] , dfn[u] + sz[u] - 1]

    (dfn代表dfs序)

    所以可以拿个数据结构来维护初始树

    T1为这个数据结构的单次查询复杂度,T2为构造复杂度

    那接下来窝们只需要回答每一个修改操作对询问操作的影响了

    第一个问题 操作1如何影响询问

    操作1是一个点权修改

    那它想影响一个询问,必须是修改这个子树里的点权

    操作2是一个建一个新的子节点,他只需要在这棵子树里建的就会影响了

    综上,我们现在得到的思路是这样的

    先用一个数据结构维护初始状态,接着对于每一个询问,扫一次它前面所有的修改操作,如果发现能够影响答案,那就将答案改变

    但这样复杂度是不对的,因为这和暴力没什么区别

    要不我们每T次操作之后,将初始状态提前一次?

    你可能没有理解这句话,就是说每T次操作之后,我们把执行T次操作得到的新树作为新的初始状态

    换句话来说,每T次操作,重新dfs+建数据结构一次

    这个时候你就发现,每一次询问往后面扫的不是M次了,而是M/T

    你很聪明知道T应该取 M\sqrt{M}

    综上,我们要实现的操作只剩下两个了

    1.写一个数据结构能够维护区间rank(就是区间[l,r][l,r]有多少个比cc大)

    2.判断一个点是否在一个子树中

    看到1窝们就想到主席树,2的话因为有修改,我们可以用倍增

    O(NlogNM)O(N * \log N * \sqrt{M}),但是这样不是最优的

    瓶颈在于倍增与主席树的构造上

    主席树构造时间复杂度是O(NlogN)O(N * \log N),一共要暴力构造M\sqrt{M}次。时间复杂度O(NlogNM)O(N * \log N * \sqrt{M})

    由于对于每一个询问操作我们都要往前扫M\sqrt{M}个修改并且每个修改花logN\log N的时间倍增,最后再加上一个主席树logN\log N查询。

    每一次询问的复杂度是O(logNM+logN)O(\log N * \sqrt{M} + \log N)

    总复杂度 O(M(logNM+logN))O(M * (\log N * \sqrt{M} + \log N))

    极大的复杂度不平衡。

    为什么一定要倍增呢?

    路径压缩难道忘了吗?

    均摊复杂度分析难道忘了吗?

    我们将倍增换成加了路径压缩的暴力,再配合一下欧拉序

    均摊分析一下复杂度

    不就成了O(MN)O(M * \sqrt{N})了吗

    再把主席树换成一个块内维护有序的分块,构造时间为O(NlogN)O(N * \log{\sqrt{N}})

    总复杂度为O(NMlogN)O(N * \sqrt{M} * \log{\sqrt{N}})

    还有许多小细节,可以看看代码

    Part3. Code

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    #include<cstring>
    using namespace std;
    
    const int MAXN = 60000 + 5;
    
    vector <int> G[MAXN];
    
    int n , m , num , f[MAXN] , sz[MAXN] , val[MAXN] , dfn[MAXN] , tp[MAXN];
    int real_node , all_node;
    
    namespace DFS{
        int dfs_clock , st[MAXN] , ed[MAXN];
        void dfs(int u , int fa) {
            sz[u] = 1 , f[u] = fa , dfn[u] = num++ , st[u] = ++dfs_clock; 
            for(int i = 0 , v ; i < (int)G[u].size() ; ++i) {
                if((v = G[u][i]) == fa) continue;
                dfs(v , u);
                sz[u] += sz[v];
            }
            ed[u] = ++dfs_clock;
        }
        inline bool check(int u , int v) { //节点v是否在以u为根的子树中 
            return st[u] <= st[v] && ed[v] <= ed[u];
        }
        void rebuild() {
            num = dfs_clock = 0;
            dfs(1 , 0);
        }
    } 
    
    namespace BLOCK{
        int SIZE , j , cnt , A[MAXN] , b[MAXN] , ord[MAXN / 100][MAXN / 100];
        void rebuild() {
            for(int i = 0 ; i < cnt ; ++i) memset(ord[i] , 0 , sizeof ord[i]);
            memset(ord[cnt] , 0 , sizeof ord[cnt]);
            SIZE = (int)sqrt(all_node + 0.5);
            j = 0 , cnt = 0;
            for(int i = 1 ; i <= all_node ; ++i) A[dfn[i]] = val[i];
            for(int i = 0 ; i < all_node ; ++i) {
                if(j == SIZE) j = 0 , ++cnt; 
                ord[cnt][j] = A[i] , b[i] = cnt;
                ++j;
            }
            for(int i = 0 ; i < cnt ; ++i) sort(ord[i] , ord[i] + SIZE);
            sort(ord[cnt] , ord[cnt] + j);
        }
        int query(int l , int r , int c) {
            int lb = b[l] , rb = b[r] , res = 0;
            if(lb == rb) {
                for(int i = l ; i <= r ; ++i) if(A[i] > c) ++res;
                return res;
            }
            for(int i = l ; i < (lb + 1) * SIZE ; ++i) if(A[i] > c) ++res;
            for(int i = rb * SIZE ; i <= r ; ++i) if(A[i] > c) ++res;
            for(int i = lb + 1 ; i < rb ; ++i) res += ord[i] + SIZE - upper_bound(ord[i] , ord[i] + SIZE , c);
            return res;
        }
    }
    
    struct Node{
        int opt , u , x , lst;
        Node(int opt = 0 , int u = 0 , int x = 0 , int lst = 0) : opt(opt) , u(u) , x(x) , lst(lst) {}
    };
    vector <Node> v;
    
    inline int real(int u) {return u <= real_node;} 
    inline int Get(int v) {
        if(real(f[v])) return f[v];
        return tp[v] = Get(f[v]);
    }
    inline bool check(int u , int v) {
        if(real(u) ^ real(v)) {
            if(real(v) == 1) return false;
            if(!tp[v]) tp[v] = Get(v);
            return DFS::check(u , tp[v]);
        }
        else {
            if(real(u) == 1) return DFS::check(u , v);
            while(!real(f[v])) {
                if(v == u) return true;
                v = f[v];
            }
            return v == u;
        }
    }
    
    int main() {
        int last_ans = 0;
        scanf("%d" , &n);
        for(int i = 1 , u , vv ; i < n ; ++i) {
            scanf("%d %d" , &u , &vv);
            G[u].push_back(vv); G[vv].push_back(u);
        }
        for(int i = 1 ; i <= n ; ++i) scanf("%d" , val + i);
        real_node = all_node = n;
        DFS::rebuild();
        BLOCK::rebuild();
        scanf("%d" , &m);int SIZE = (int)sqrt(m + 0.5) + 1 , j = 0;
        while(m--) {
            int opt , u , x;
            scanf("%d %d %d" , &opt , &u , &x);
            if(j == SIZE) {
                memset(tp , 0 , sizeof tp);
                for(int i = 0 ; i < (int)v.size() ; ++i) 
                    if(v[i].opt == 2) G[v[i].lst].push_back(++real_node);
                DFS::rebuild(); BLOCK::rebuild();
                v.clear();
                j = 0;
            }
            u ^= last_ans , x ^= last_ans;
            if(opt == 0) {
                int ans = BLOCK::query(dfn[u] , dfn[u] + sz[u] - 1 , x);
                for(int i = 0 ; i < (int)v.size() ; ++i) {
                    if(check(u , v[i].u)) {
                        if(v[i].opt == 1) {
                            if(v[i].lst > x && v[i].x <= x) --ans;
                            if(v[i].lst <= x && v[i].x > x) ++ans;
                        }
                        if(v[i].opt == 2)
                            ans += v[i].x > x;
                    }
                }
                printf("%d\n" ,	last_ans = ans); 
            }
            else if(opt == 1) {
                v.push_back(Node(opt , u , x , val[u]));
                val[u] = x;
            }
            else {
                v.push_back(Node(opt , ++all_node , x , u));
                val[all_node] = x; f[all_node] = u;
            }
            ++j;
        }
        return 0;
    } 
    
    • 1

    信息

    ID
    1121
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者