bzoj2212[Poi2011]Tree Rotations [线段树合并]

题面

bzoj

ans = 两子树ans + min(左子在前逆序对数, 右子在前逆序对数)
线段树合并

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <cstring>
#define Sqr(x) ((x)*(x))
using namespace std;
const int N = 2e5 + 5;
const int M = 4e6 + 5;
long long cnt1, cnt2;
struct Seg{
    int w[M], sz, ls[M], rs[M];
    void ins(int l, int r, int x, int &rt){
        if(!rt) rt = ++sz;
        if(l == r) {w[rt] = 1; return ;}
        int mid = l + ((r - l) >> 1);
        if(x <= mid) ins(l, mid, x, ls[rt]); 
        else ins(mid + 1, r, x, rs[rt]);
        w[rt] = w[ls[rt]] + w[rs[rt]];
    }
    int merge(int x, int y){
        if(!x || !y) return x + y;
        cnt1 += 1ll * w[ls[x]] * w[rs[y]];
        cnt2 += 1ll * w[rs[x]] * w[ls[y]];
        ls[x] = merge(ls[x], ls[y]);
        rs[x] = merge(rs[x], rs[y]);
        w[x] = w[ls[x]] + w[rs[x]];
        return x;
    }
}seg;
int n, m, sz, rt[N << 2], l[N << 2], r[N << 2], w[N << 2];

void init(int &cur){
    cur = ++sz;
    scanf("%d", &w[cur]);
    if(!w[cur]) {init(l[cur]); init(r[cur]);}
    else seg.ins(1, n, w[cur], rt[cur]);
}

long long solve(int cur){
    if(w[cur]) return 0;//如果不在叶子节点停下 叶子就会被合并成空树 
    long long res = solve(l[cur]) + solve(r[cur]);
    //printf("%d %lld %d %d\n", cur, res, l[cur], r[cur]);
    cnt1 = cnt2 = 0;
    rt[cur] = seg.merge(rt[l[cur]], rt[r[cur]]);
    //printf("%lld %lld\n", cnt1, cnt2);
    return res + min(cnt1, cnt2);
}

int main() {
    scanf("%d", &n);
    init(m);
    printf("%lld\n", solve(m));
    //system("PAUSE");
    return 0;
}
/*
检查所有的int函数是否有返回值 
*/

猜你喜欢

转载自www.cnblogs.com/hjmmm/p/10654632.html