bzoj 2212 Tree Rotations

bzoj 2212 Tree Rotations

  • 考虑一个子树 \(x\) 的左右儿子分别为 \(ls,rs\) .那么子树 \(x\) 内的逆序对数就是 \(ls\) 内的逆序对数,\(rs\) 内的逆序对数,跨越 \(ls,rs\) 的逆序对数三者之和.
  • 交换 \(ls,rs\) 显然对前两种的答案没有影响,只需最大化最后一种答案.
  • 对每个叶子节点开一棵权值线段树向上合并,选取权值中点 \(mid\) 划分开,那么两种情况在当前层产生的贡献即为 \(ls\) 的左子树大小 \(\times\) \(rs\) 的右子树大小,\(ls\) 的右子树大小 \(\times\) \(rs\) 的左子树大小中的最大值.
  • 继续递归合并,就可以计算到所有贡献.
  • 时间复杂度为 \(O(nlogn)\) .
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline int read()
{
    int out=0,fh=1;
    char jp=getchar();
    while ((jp>'9'||jp<'0')&&jp!='-')
        jp=getchar();
    if (jp=='-')
        fh=-1,jp=getchar();
    while (jp>='0'&&jp<='9')
        out=out*10+jp-'0',jp=getchar();
    return out*fh;
}
const int MAXN=2e5+10;
ll ans=0,ans1,ans2;
struct node{
    int siz,ls,rs;
}Tree[MAXN*30];
#define root Tree[o]
#define lson Tree[root.ls]
#define rson Tree[root.rs]
#define t(x) Tree[x]
int n,cnt=0;
void update(int &o,int l,int r,int pos)
{
    if(!o)
        o=++cnt;
    ++root.siz;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    if(pos<=mid)
        update(root.ls,l,mid,pos);
    else
        update(root.rs,mid+1,r,pos);
}
int merge(int ls,int rs)
{
    if(!ls || !rs)
        return ls+rs;
    t(ls).siz+=t(rs).siz;
    ans1+=1LL*(Tree[t(ls).ls].siz)*(Tree[t(rs).rs].siz);
    ans2+=1LL*(Tree[t(ls).rs].siz)*(Tree[t(rs).ls].siz);
    t(ls).ls=merge(t(ls).ls,t(rs).ls);
    t(ls).rs=merge(t(ls).rs,t(rs).rs);
    return ls;
}
int solve()
{
    int p=read(),x=0;
    if(!p)
        {
            int ls=solve();
            int rs=solve();
            ans1=ans2=0;
            x=merge(ls,rs);
            ans+=min(ans1,ans2);
            return x;
        }
    else
        update(x,1,n,p);
    return x;
}
int main()
{
    n=read();
    solve();
    cout<<ans<<endl;
    return 0;
}

转载于:https://www.cnblogs.com/jklover/p/10606974.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值