牛客挑战赛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 
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;
}
View Code

 

你可能感兴趣的:(牛客挑战赛32 E. 树上逆序对)