牛客挑战赛32 E. 树上逆序对
对于一对 $(x, y)$,能成为逆序对的取决于绝对值大的那个数的符号。
假如 $a[x] > a[y]$,当 $a[x]$ 为正时,不管 $a[y]$ 取不取负号都比 $a[x]$ 小。
当 $a[x]$ 为负时, 不管 $a[y]$ 取不取负号都比 $a[x]$ 大。
那么就变成了统计每个节点的子树及祖先有多少个比它的权值小的。取正时,子树内权值比它小的节点对答案有贡献,取负时,祖先中权值比它的节点对答案有贡献。
然后就相当于01背包了。
用bitset优化一下复杂度就是 $O(\dfrac{nk}{64})$
求子树内和到根的路径上比该节点小的数可以树剖做。但看了tangjz的代码发现一个更加巧妙的方法。
求子树内显然可以从小到大插入节点+dfs序+树状数组。
求该节点到根的路径上比自己权值大的,就相当于求自己有几个祖先,相当于求自己在多少个节点的子树内。
那么多开一个树状数组,在插入一个节点时,让其子树这个区间(不包括自身)区间加一。查询时单点查值就行了。
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 7; bitset<30001> mask; int n; struct BIT { int tree[N]; inline int lowbit(int x) { return x & -x; } void add(int x, int v) { if (!x) return; for (int i = x; i <= n; i += lowbit(i)) tree[i] += v; } int query(int x) { int ans = 0; for (int i = x; i; i -= lowbit(i)) ans += tree[i]; return ans; } } bit[2]; int a[N], in[N], out[N], tol, o[N]; vector<int> G[N]; void dfs(int u, int fa) { in[u] = ++tol; for (auto v: G[u]) if (v != fa) dfs(v, u); out[u] = tol; } bool cmp(const int &x, const int &y) { return a[x] < a[y]; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); o[i] = i; } sort(o + 1, o + n + 1, cmp); for (int i = 1; i < n; i++) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } dfs(1, -1); mask.set(0); for (int i = 1; i <= n; i++) { int u = o[i]; int k1 = bit[0].query(in[u]), k2 = bit[1].query(out[u]) - bit[1].query(in[u] - 1); mask = mask << k1 | mask << k2; bit[0].add(in[u] + 1, 1); bit[0].add(out[u] + 1, -1); bit[1].add(in[u], 1); } int q; scanf("%d", &q); while (q--) { int k; scanf("%d", &k); puts(mask.test(k) ? "Orz" : "QAQ"); } return 0; }

浙公网安备 33010602011771号