• Codeforces 1585E Frequency Queries


    题目大意

    给定一棵 \(n(n\leq 10^6)\) 个点的树,结点 \(u\) 的点权为 \(a_u(1\leq a_u\leq n)\)\(q(q\leq 10^6)\) 次询问,每次询问 \(u,l,k\),表示从 \(u\) 到根的树链上所有点权拿出来,把点权按出现次数从小到大排序再去重,去掉出现次数少于 \(l\) 的点权,剩下的数组里的第 \(k\) 个点权是多少。答案可能有多解,输出任意一个,若无解,输出 \(-1\)

    题解

    首先询问可以离线,所以可以每遍历到一个点就处理关于这个点的所有询问。

    \(cnt[x]\) 表示点权 \(x\) 出现的数目。在DFS的过程中很容易可以维护当前点 \(u\) 到根的路径上所有的点权的 \(cnt[x]\)

    \(p\)\(1\sim n\) 的一个排列,表示点权 \(1\sim n\) 按在 \(u\) 到根的路径上出现的次数从小到大排序的结果,设 \(p^{-1}\) 表示 \(p\) 的逆排列,即 \(p^{-1}[p[i]]=i\),设 \(lb_x\) 表示第一个出现次数大于等于 \(x\) 的点权在 \(p\) 中的下标。我们只要维护 \(cnt,p,p^{-1},lb\) 数组,最后询问 \(u,l,k\) 的答案即为 \(p[lb[l]+k-1]\)

    当DFS进入一个点 \(u\) 时,我们令 \(x:=a[u]\)。此时 \(p\) 一定是被分为若干段,每一段内的点权出现的次数相等,段与段之间点权出现的次数相差1。那么我们需要考虑 \(++cnt[x]\) 时对 \(p,p^{-1},lb\) 的影响。我们可以把 \(p\)\(x\) 的位置和 \(x\) 所在段的最后一个数的位置互换,然后让当前段的最后一个位置变成下一段的第一个位置即可,即:

    int x = a[u];
    int p1 = pt[x], p2 = lb[cnt[x] + 1] - 1;
    swap(p[p1], p[p2]);
    int pt1 = x, pt2 = p[pt[x]];
    swap(pt[pt1], pt[pt2]);
    --lb[cnt[x] + 1]; ++cnt[x];
    

    当退出 \(u\) 结点时需要恢复影响:

    --cnt[x]; ++lb[cnt[x] + 1];
    swap(pt[pt1], pt[pt2]);
    swap(p[p1], p[p2]);
    

    时间复杂度 \(O(n+q)\)

    感觉这个单点 \(O(1)\) 修改挺巧妙的。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    
    template<typename elemType>
    inline void Read(elemType& T) {
        elemType X = 0, w = 0; char ch = 0;
        while (!isdigit(ch)) { w |= ch == '-';ch = getchar(); }
        while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();
        T = (w ? -X : X);
    }
    
    const int maxn = 1000010;
    
    vector<int> G[maxn];
    struct Node { int id, l, k; };
    vector<Node> ask[maxn];
    int a[maxn], ans[maxn], cnt[maxn], p[maxn], pt[maxn], lb[maxn];
    int T, n, q;
    
    void DFS(int u, int fa) {
        int x = a[u];
        int p1 = pt[x], p2 = lb[cnt[x] + 1] - 1;
        swap(p[p1], p[p2]);
        int pt1 = x, pt2 = p[pt[x]];
        swap(pt[pt1], pt[pt2]);
        --lb[cnt[x] + 1]; ++cnt[x];
        for (auto node : ask[u]) {
            if (lb[node.l] + node.k - 1 > n) ans[node.id] = -1;
            else ans[node.id] = p[lb[node.l] + node.k - 1];
        }
        for (auto v : G[u]) {
            if (v == fa) continue;
            DFS(v, u);
        }
        --cnt[x]; ++lb[cnt[x] + 1];
        swap(pt[pt1], pt[pt2]);
        swap(p[p1], p[p2]);
    }
    
    int main() {
        Read(T);
        while (T--) {
            Read(n);Read(q);
            lb[0] = 1;
            for (int i = 1;i <= n;++i) { G[i].clear(); p[i] = pt[i] = i; lb[i] = n + 1; cnt[i] = 0; }
            for (int i = 1;i <= n;++i) { Read(a[i]); ask[i].clear(); }
            for (int i = 2;i <= n;++i) { int u; Read(u); G[u].push_back(i); G[i].push_back(u); }
            for (int i = 1;i <= q;++i) {
                int v, l, k;
                Read(v);Read(l);Read(k);
                ask[v].push_back((Node) { i, l, k });
            }
            DFS(1, 0);
            for (int i = 1;i <= q;++i)
                printf("%d ", ans[i]);
            printf("\n");
        }
    
        return 0;
    }
    
  • 相关阅读:
    Android基础总结(四)网络通信
    Android基础总结(三)SQLite,ListView,对话框
    am335x sd卡启动系统参数设置
    am335x 电容屏驱动添加。
    linux 下恢复后台程序的方法
    qt-5.6.0 移植之实现板子与ubuntu主机通过网络进行文件传输
    qt-5.6.0 移植之qt文件系统的建立
    qt-5.6.0 移植之tslib 配置及编译
    qt-5.6.0 移植之qt源码编译
    qt-5.6.0 移植之纯净的linux文件系统的建立
  • 原文地址:https://www.cnblogs.com/AEMShana/p/15731704.html
Copyright © 2020-2023  润新知