luogu P3605 USACO17JAN Promotion Counting P

P3605 USACO17JAN Promotion Counting P - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

注意到本题权值保证两两不同。

本文中 \(a\) 数组相当于原题的 \(p\) 数组。设下文所有 \(w\) 代表 \(a_u\)

思路一:dfs。处理到点 \(u\) 前,总共有 \(p\) 个权值比 \(w\) 大的节点,递归处理点 \(u\) 及其子树后,总共有 \(q\) 个权值比 \(w\) 大的节点,那么 \(u\) 的子树中有 \(q - p\) 个权值比 \(w\) 大的节点。

将节点的权值离散化后,插入树状数组来维护。发现容易维护的是权值比 \(w\) 小的节点数。

有以下两种方案:

  • 权值比 \(w\) 大的节点数等于目前所有节点数减去小于等于 \(w\) 的权值的节点数,简单作差;
  • 给离散化数组的所有元素 \(x\) 变为 \(n + 1 - x\),以翻转所有元素间的大小关系,然后直接做。

个人选用第二种。

思路二:数组 \(b[dfn(u)]\) 表示点 \(u\) 是否已被标记,取值 \(\{1, 0\}\)。点 \(u\) 和其子树的 dfn 一定是连续的一段,具体来说是 \([dfn(u), dfn(u) + siz(u) - 1]\),那么可以查询 \(b\) 的这段区间的和,表示点 \(u\) 及其子树的点有多少个已被标记。按照 \(u\) 的权值大到小标记 \(b[dfn(u)]\)(即设置为 \(1\)),那么一个点已被标记等价于一个点权值比 \(w\) 大,边查询边标记即可。对 \(b\) 使用树状数组加速。

思路一实现较为简单,且对权值的互异性无依赖。思路二如果想在权值不保证互异性时仍然可处理,需要一次性标记好所有权值相同的点然后再分别查询它们。

两种思路时间复杂度均为 \(\mathcal{O}(n\log n)\)

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2022-10-20 15:40:23 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2022-10-20 16:19:27
 */
#include <bits/stdc++.h>
#define int long long
inline int read() {
    int x = 0;
    bool flag = true;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-')
            flag = false;
        ch = getchar();
    }
    while (isdigit(ch)) {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    if(flag)
        return x;
    return ~(x - 1);
}
inline int lowbit(int x) {
    return x & (-x);
}
const int maxn = (int)1e5 + 5;

int a[maxn], au[maxn];
std :: vector <int> G[maxn];

int c[maxn];

int n;
inline void add(int x) {
    for (; x <= n; x += lowbit(x))
        ++c[x];
    return ;
}

inline int get(int x) {
    int sum = 0;
    for (; x; x -= lowbit(x))
        sum += c[x];
    return sum;
}

int ans[maxn];
void dfs(int u) {
    ans[u] = -get(a[u]);
    for (int v : G[u])
        dfs(v);
    ans[u] += get(a[u]);
    // 这里有个细节,为什么可以直接 get(a[u]) 呢;
    // 这难道不是直接获取权值小于等于 a[u] 的点的吗?
    // 的确,我们的需求是小于;
    // 但是本题中保证权值互异;
    // 权值等于 a[u] 的点只有 u 自己;
    // 因此只需要考虑 u 的影响,那在查询完之后再标记 a[u] 即可。

    // 当然可以不想这么多。。直接 get(a[u] - 1) 即可。
    add(a[u]);
}

signed main() {
    n = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read();
    for (int i = 2; i <= n; ++i) {
        int x = read();
        G[x].push_back(i);
    }

    std :: copy(a + 1, a + 1 + n, au + 1);
    std :: sort(au + 1, au + 1 + n);
    auto en = std :: unique(au + 1, au + 1 + n);
    
    for (int i = 1; i <= n; ++i)
        a[i] = n + 1 - (std :: lower_bound(au + 1, en, a[i]) - au);
    
    dfs(1);

    for (int u = 1; u <= n; ++u)
        printf("%lld\n", ans[u]);
    
    return 0;
}
posted @ 2022-10-20 16:23  dbxxx  阅读(25)  评论(0编辑  收藏  举报