POI2011 Tree Rotations 2

Tree Rotations 2

POI2011
怎么可以数据范围大9倍,内存才大1倍啊,对树套树说さようなら(再见)吧

题意

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

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

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

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

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

1.启发式合并Splay
这里必须按照特别的顺序合并,复杂度我也不能证明,总之网上说是 n l o g n nlog{n} nlogn的,意会一下,这样插入是翻转的次数应该会比较少吧

一样是遍历小树,但是必须从小到大合并进去
把小树按照中序遍历插入到大树中去

在插入时,可以顺便求出有多少个比刚插入的数大的数(因为刚插入的数被旋到了根,很容易求)
ans+=min(cnt,sum-cnt);

具体代码

#include<bits/stdc++.h>
using namespace std;
const int M=1000005;
long long ans;
int n,ID,root[M*2];
int fa[M],son[M][2],sz[M];
void rd(int &res) {
    res=0;
    char c;
    while(c=getchar(),c<48);
    do res=(res<<3)+(res<<1)+(c&15);
    while(c=getchar(),c>47);
}
bool get(int x) {
    return son[fa[x]][1]==x;
}
void Up(int x) {
    sz[x]=sz[son[x][0]]+sz[son[x][1]]+1;
}
void rotate(int x) {
    int dir=!get(x),y=fa[x];
    son[y][!dir]=son[x][dir];
    fa[son[x][dir]]=y;
    if(fa[y])son[fa[y]][get(y)]=x;
    fa[x]=fa[y];
    son[x][dir]=y,fa[y]=x;
    Up(y),Up(x);
}
void splay(int x,int rt) {
    while(fa[x]!=0) {
        int y=fa[x];
        if(fa[y]==0)rotate(x);
        else {
            if(get(x)==get(y))rotate(y);
            else rotate(x);
            rotate(x);
        }
    }
    root[rt]=x;
}
void insert(int rt,int val) {
    int x=root[rt];
    for(;; x=son[x][x<val]) {
        if(!son[x][x<val])break;
    }
    son[x][x<val]=val;
    fa[val]=x,sz[val]=1;
    son[val][0]=son[val][1]=0;
    splay(val,rt);
}
int a;
long long cnt,sum;
void merge(int x,int rt) {
    if(son[x][0])merge(son[x][0],rt);
    int RR=son[x][1];
    insert(rt,x);
    cnt+=sz[son[root[rt]][1]];
    if(RR)merge(RR,rt);
}
void dfs(int x) {
    rd(a);
    if(a!=0) {
        root[x]=a;
        fa[a]=0,sz[a]=1;
        son[a][0]=son[a][1]=0;
    } else {
        int p1=++ID;
        dfs(p1);
        int p2=++ID;
        dfs(p2);
        if(sz[root[p1]]>sz[root[p2]])swap(p1,p2);
        sum=1ll*sz[root[p1]]*sz[root[p2]];
        cnt=0;
        merge(root[p1],p2);
        ans+=min(cnt,sum-cnt);
        root[x]=root[p2];
    }
}
int main() {
    rd(n);
    dfs(ID=1);
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值