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函数是否有返回值 
*/
posted @ 2019-04-04 14:23  hjmmm  阅读(85)  评论(0编辑  收藏  举报