POI2011 Tree Rotations

Tree Rotations

POI2011

题意

1.有一棵二叉树,所有非叶子节点都有两个孩子

2.每个叶子节点有一个权值(有n个叶子节点,这些权值是1~n的一个排列)

3.现在可以任意交换每个非叶子节点的左右孩子

4.要求进行一系列交换,使得最终所有叶子节点的权值按照遍历序写出来,逆序对个数最少

5.问逆序对个数最少为多少

方案1(树套树)

注意这个方法不能过Tree Rotations2 因为内存不够

1.对于每个点为根的树,求出这时的最小逆序对个数

2.可以随意交换这个点的两个叶子节点,那么子树内部的逆序对是没有影响的,只要考虑这两个子树之间的产生的贡献

3.主席树
每次遍历到叶子节点时,在主席树中加入这个点

4.启发式合并
枚举小树的点,(在主席树上)查询另一棵树上有多少个数比该点大
ans+=min(cnt,sz1*sz2-cnt)

具体代码

#include<bits/stdc++.h>
using namespace std;
const int M=200001;
long long ans;
int ID,n,A[M],len;
int LL[M*2],RR[M*2];
int tot,root[M],Ls[M*21],Rs[M*21],sum[M*21];
void build(int &rt,int L,int R) {
    rt=++tot;
    sum[rt]=0;
    if(L==R)return;
    int mid=L+R>>1;
    build(Ls[rt],L,mid);
    build(Rs[rt],mid+1,R);
}
void update(int &rt,int pt,int x,int L,int R) {
    rt=++tot;
    sum[rt]=sum[pt]+1;
    Ls[rt]=Ls[pt],Rs[rt]=Rs[pt];
    if(L==R)return;
    int mid=L+R>>1;
    if(x<=mid)update(Ls[rt],Ls[pt],x,L,mid);
    else update(Rs[rt],Rs[pt],x,mid+1,R);
}
int query(int rt,int pt,int l,int r,int L,int R) {
    if(l==L&&R==r)return sum[rt]-sum[pt];
    int mid=L+R>>1;
    if(r<=mid)return query(Ls[rt],Ls[pt],l,r,L,mid);
    else if(mid<l)return query(Rs[rt],Rs[pt],l,r,mid+1,R);
    else return query(Ls[rt],Ls[pt],l,mid,L,mid)+query(Rs[rt],Rs[pt],mid+1,r,mid+1,R);
}
int a;
int Sz(int x){
    return RR[x]-LL[x]+1;
}
void dfs(int x) {
    scanf("%d",&a);
    if(a!=0) {
        LL[x]=RR[x]=++len;
        A[len]=a;
        update(root[len],root[len-1],a,1,n);
    } else {
        int p1=++ID;
        dfs(ID);
        int p2=++ID;
        dfs(ID);
        LL[x]=LL[p1],RR[x]=RR[p2];
        if(Sz(p1)>Sz(p2))swap(p1,p2);
        //p1-->p2
        long long cnt=0;
        for(int i=LL[p1]; i<=RR[p1]; i++) {
            cnt+=query(root[RR[p2]],root[LL[p2]-1],1,A[i],1,n);
        }
        ans+=min(cnt,1ll*Sz(p1)*Sz(p2)-cnt);
    }
}
int main() {
    scanf("%d",&n);
    build(root[0],1,n);
    dfs(ID=1);
    printf("%lld\n",ans);
    return 0;
}
方案2(Splay)

这个方法对于Tree Rotations2也适用,详见Tree Rotations2的题解
主要就是Splay省空间

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值