校内训练0531 逆序对

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了校内训练0531 逆序对相关的知识,希望对你有一定的参考价值。

【题目大意】

有一棵2n-1个节点的二叉树,它有恰好n个叶子节点,每个叶子节点上写了一个整数。如果将这棵树的所有叶子节点上的数从左到右写下来,便得到一个序列a[1]…a[n]。现在想让这个序列中的逆序对数量最少,但唯一的操作就是选树上一个非叶子节点,将它的左右两颗子树交换。你可以做任意多次这个操作。求在最优方案下,该序列的逆序对数最少有多少。

n<=200000

【题解】
啊很明显我们对于每个节点 判断一下两边交换/不交换哪个逆序对贡献的少就行了

至于这个逆序对贡献啊?线段树合并!

啊我不会线段树合并啊?启发式合并(dsu on tree)

技术分享
# include <vector>
# include <stdio.h>
# include <assert.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int M = 5e5 + 10;
const int mod = 1e9+7;

# define RG register
# define ST static

int n, siz, rt, ch[M][2], siz2;
int val[M], a[M], m, sz[M], sz2[M];
vector<int> ps;

struct BIT {
    int c[M], n;
    # define lb(x) (x&(-x))
    inline void set(int _n) {
        n = _n;
        memset(c, 0, sizeof c);
    }
    inline void edt(int x, int d) {
        for (; x<=n; x+=lb(x)) c[x] += d;
    }
    inline int sum(int x) {
        int ret=0;
        for (; x; x-=lb(x)) ret += c[x];
        return ret;
    }
    inline int sum(int l, int r) {
        if(l>r) return 0;
        return sum(r)-sum(l-1);
    }
}T;

inline void dfs(int &x) {
    int t; scanf("%d", &t);
    if(t == 0) x = ++siz;
    else x = ++siz2;
    sz[x] = 1;
    if(t == 0) {
        dfs(ch[x][0]);
        dfs(ch[x][1]);
        sz[x] += sz[ch[x][0]] + sz[ch[x][1]];
        sz2[x] = sz2[ch[x][0]] + sz2[ch[x][1]];
    } else {
        sz2[x] = 1;
        val[x] = t;
        ps.push_back(t);
    }    
}

bool big[M];
ll cura = 0, curb;
inline void calc(int x) {
    if(val[x] != 0) {
        cura += T.sum(val[x]+1, n);
        curb += T.sum(val[x]-1);
        return ;
    }
    if(!big[ch[x][0]]) calc(ch[x][0]);
    if(!big[ch[x][1]]) calc(ch[x][1]);
}

inline void del(int x, int d) {
    if(x <= n) {
        T.edt(val[x], d);
        return ;
    }
    if(!big[ch[x][0]]) del(ch[x][0], d);
    if(!big[ch[x][1]]) del(ch[x][1], d);
}

ll ans = 0;
inline void dsu(int x, bool kep) {
    if(x <= n) {
        if(kep) T.edt(val[x], 1);
        return;
    }
    int bc, sc;
    if(sz[ch[x][0]] > sz[ch[x][1]]) bc = ch[x][0], sc = ch[x][1];
    else bc = ch[x][1], sc = ch[x][0];
    dsu(sc, 0);
    dsu(bc, 1);
//    printf("x=%d, query=%d\n", x, T.sum(1, n));
    big[bc] = 1;
    cura = curb = 0;
    calc(x); del(x,1);
    ans += min(cura, curb);
    big[bc] = 0; 
    if(kep == 0) del(x,-1);        
}

int main() {
    cin >> n; 
    siz = n; dfs(rt);
    T.set(n);
    sort(ps.begin(), ps.end());
    ps.erase(unique(ps.begin(), ps.end()), ps.end());
    for (int i=1; i<=siz; ++i)
        if(val[i] != 0) val[i] = lower_bound(ps.begin(), ps.end(), val[i]) - ps.begin() + 1;
//    for (int i=1; i<=siz; ++i) printf("x=%d, ls=%d, rs=%d\n", i, ch[i][0], ch[i][1]);
    dsu(rt, 1);
    cout << ans << endl;
    return 0;
}
View Code

 


以上是关于校内训练0531 逆序对的主要内容,如果未能解决你的问题,请参考以下文章

fzyjojP2963 -- [校内训练20161227]疫情控制问题

2017-4-7校内训练

校内训练2019-11-15跳一跳

校内训练2019-11-15逮虾户

校内训练2019-11-15表演

算法训练 逆序对