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;
}