BZOJ 2212 & POI 18 Tree Rotations(线段树合并)

来源:互联网 发布:python pyqt5实例 编辑:程序博客网 时间:2024/06/05 04:49
题意:给定一棵2n-1个节点的二叉树, 每个叶子上有1~n的数字, 保证每个数字出现且仅出现一次

 允许任意次交换某两棵兄弟子树

 对交换完毕的树求先序遍历, 形成1~n的一个排列

 求这个排列最小的逆序对个数

 1 ≤ n ≤ 2 * 1e5 (1e6)



思路:子树x内的逆序对个数为 :x 左子树内的逆序对个数 + x 右子树内的逆序对个数 + 跨越 x 左子树与右子树的逆

序对数。可以发现左右子树内部的逆序对与是否交换左右子树无关,是否交换左右子树取决于交换后 “跨越 x 左子树

与右子树的逆序对” 是否会减小。


可以用一些统计区间内数字个数 (cnt) 的线段树的合并来解决

在merge的过程中统计交换与不交换产生的逆序对数

a属于T的左子树, b属于T的右子树, ans0为不交换产生的逆序对数, ans1为交换产生的逆序对数

merge(a, b):
如果a, b中有一个为空, 就返回另一个
ans0 += cnt(a -> r) * cnt(b -> l)
ans1 += cnt(a -> l) * cnt(b -> r)
返回merge(a->l, b->l)与merge(a->r, b->r)连接形成的树的根


代码:

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;typedef long long ll;const int maxn = 4e5+5;const int maxnode = 4e6+5;int n, sz, seg;ll ans, ans0, ans1;int tree[maxnode], lch[maxnode], rch[maxnode];int v[maxn], l[maxn], r[maxn], root[maxn];void init(){    sz = 1;    ans = seg = 0;    memset(v, 0, sizeof(v));    memset(root, 0, sizeof(root));    memset(tree, 0, sizeof(tree));    memset(lch, 0, sizeof(lch));    memset(rch, 0, sizeof(rch));}void readTree(int x){    scanf("%d", &v[x]);    if(!v[x])    {        l[x] = ++sz;        readTree(l[x]);        r[x] = ++sz;        readTree(r[x]);    }}void pushup(int rt){    tree[rt] = tree[lch[rt]]+tree[rch[rt]];}void build(int &rt, int l, int r, int val){    if(!rt) rt = ++seg;    if(l == r)    {        tree[rt] = 1;        return ;    }    int mid = (l+r)/2;    if(val <= mid) build(lch[rt], l, mid, val);    else build(rch[rt], mid+1, r, val);    pushup(rt);}int merge(int x, int y){    if(!x) return y;    if(!y) return x;    ans0 += (ll)tree[rch[x]]*tree[lch[y]];    ans1 += (ll)tree[lch[x]]*tree[rch[y]];    lch[x] = merge(lch[x], lch[y]);    rch[x] = merge(rch[x], rch[y]);    pushup(x);    return x;}void solve(int x){    if(!x) return ;    solve(l[x]), solve(r[x]);    if(!v[x])    {        ans0 = ans1 = 0;        root[x] = merge(root[l[x]], root[r[x]]);        ans += min(ans0, ans1);    }}int main(void){    while(cin >> n)    {        init();        readTree(1);        for(int i = 1; i <= sz; i++)            if(v[i]) build(root[i], 1, n, v[i]);        solve(1);        printf("%lld\n", ans);    }    return 0;}


阅读全文
1 0