• 【数据结构】树上莫队


    CF375D - Tree and Queries

    题意:给定一棵 (n(2leq nleq 10^5)) 个结点的树,根结点为 (1) 号结点,每个结点有颜色 (c(leq cleq 10^5)) 。然后 (m(1leq mleq 10^5)) 次询问,每次询问给两个参数 ((v,k)) ,询问 (v) 子树内,频次至少有 (k) 次的颜色有多少种。

    题解:经典计数颜色的数量,使用莫队算法。不过这一次颜色的数量 (k) 不确定,也要作为一个维度进行转移。这一次查询的并非树链而是子树,所以结点出栈的时候不需要删除这个结点的信息。对每个询问给一个区间 ([fst[v],lst[v]]) 查询频次至少有 (k) 次的颜色有多少种。考虑带修改的莫队算法,块的大小为 (sqrt[3]{nk}) ,总复杂度为 (O(sqrt[3]{n^4k})) 。但是实测表明开 (sqrt[2]{n}) 更快,不知道原因是什么,可能和这里的区间并非任意指定有关(是dfs序的区间)。

    namespace MoAlgorithmOnTree {
    
        static const int MAXM = 1e5 + 10;
        static const int MAXN = 1e5 + 10;
        static const int MAXC = 1e5 + 10;
    
        int n, m, BLOCK;
        vector<int> G[MAXN];
        int id[MAXN], idt, fst[MAXN], lst[MAXN];
    
        void dfs(int u, int p) {
            id[++idt] = u;
            fst[u] = lst[u] = idt;
            for (int v : G[u]) {
                if (v == p)
                    continue;
                dfs(v, u);
                lst[u] = lst[v];
            }
        }
    
        struct Node {
            int l, r, k, id;
            bool operator<(const Node &node) const {
                if (l / BLOCK != node.l / BLOCK)
                    return l < node.l;
                if (r / BLOCK != node.r / BLOCK)
                    return r < node.r;
                return k < node.k;
            }
        } node[MAXM];
        ll ans[MAXM];
    
        int L, R, K;
        ll curans;
    
        int c[MAXN], cnt[MAXC], sum[MAXN];
    
        void add(int x) {
            --sum[cnt[c[id[x]]]];
            ++cnt[c[id[x]]];
            ++sum[cnt[c[id[x]]]];
            if (cnt[c[id[x]]] == K)
                ++curans;
        }
    
        void sub(int x) {
            if (cnt[c[id[x]]] == K)
                --curans;
            --sum[cnt[c[id[x]]]];
            --cnt[c[id[x]]];
            ++sum[cnt[c[id[x]]]];
        }
    
        void addK() {
            curans -= sum[K];
            ++K;
        }
    
        void subK() {
            --K;
            curans += sum[K];
        }
    
        void Solve() {
            ms(cnt), ms(sum);
            scanf("%d%d", &n, &m);
            for (int i = 1; i <= n; ++i)
                G[i].clear();
            for (int i = 1; i <= n; ++i)
                scanf("%d", &c[i]);
            for (int i = 1; i <= n - 1; ++i) {
                int u, v;
                scanf("%d%d", &u, &v);
                G[u].eb(v);
                G[v].eb(u);
            }
            idt = 0;
            dfs(1, 0);
            BLOCK = (int)ceil(pow(n, 0.667));
            for (int i = 1; i <= m; ++i) {
                int v, k;
                scanf("%d%d", &v, &k);
                node[i].l = fst[v];
                node[i].r = lst[v];
                node[i].k = k;
                node[i].id = i;
            }
            sort(node + 1, node + 1 + m);
            L = 1, R = 0, K = 0, curans = n, sum[0] = n;
            for (int i = 1; i <= m; ++i) {
                while (L > node[i].l)
                    --L, add(L);
                while (R < node[i].r)
                    ++R, add(R);
                while (L < node[i].l)
                    sub(L), ++L;
                while (R > node[i].r)
                    sub(R), --R;
                while (K < node[i].k)
                    addK();
                while (K > node[i].k)
                    subK();
                ans[node[i].id] = curans;
            }
            for (int i = 1; i <= m; ++i)
                printf("%lld
    ", ans[i]);
        }
    
    };
    

    但是换一种思路,其实K没有必要作为其中一个维度,不过就不再维护curans,或者说,curans是一个数组,curans[i]表示频次大于等于i的颜色数。考虑一种颜色频次从cnt增加到cnt+1时,只会让大于等于cnt+1的答案+1,对于大于等于cnt其实是没有变化的。

    这道题也可以树上启发式合并做。

  • 相关阅读:
    C# WinForm TextBox 作为密码输入框时,如何禁止密码查看器获取密码 ?
    .net 程序运行在不同框架版本下的支持配置(主要是.net4.0 与 .net2.0的兼容)
    比较C#的静态常量(const)和动态常量(static和readonly)
    Linux 本地yum源 、阿里yum源、163yum源的配置安装
    Mysql 单机数据库迁移几种方式
    sed中使用变量及变量中存在特殊字符‘/’处理
    Linux下安装zookeeper、配置zookeeper开机自启动
    MySQL 不同场景下的迁移方案(转载)
    配置YUM源出现Errno 14 Could not open/read repomd.xml 或者 "Couldn't open file /mnt/cdrom/repodata/repomd.xml" 错误的解决办法
    Docker安装Rabbitmq并实现挂载宿主机数据目录
  • 原文地址:https://www.cnblogs.com/purinliang/p/14544746.html
Copyright © 2020-2023  润新知