【BZOJ2212】[POI2011]Tree Rotations (线段树合并)

本文详细解析了BZOJ2212 TreeRotations问题,通过分析二叉树的逆序对数,采用线段树合并策略,实现了高效的逆序对计算,提供了一种从下往上的启发式合并解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【BZOJ2212】[POI2011]Tree Rotations (线段树合并)

题面

BZOJ
洛谷

题解

因为是一棵二叉树,我们发现对于左右儿子而言只有两种放法。
不考虑左右儿子内部的相对顺序,那么发现两个儿子先后顺序的逆序对数是固定的,而确定好顺序之后显然就是一个分治的过程。
那么显然每次决策都是选择放在前面后,左右之间贡献的逆序对数较少的那一边。
考虑这个过程如何实现,从上往下做显然不好做。
考虑从下往上合并,显然启发式合并是可行的,但是这样复杂度会多出一个\(log\)。我们发现可以在线段树合并的过程中维护逆序对,这样子复杂度就变成了一个\(log\)

#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MAX 400200
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
struct Node{int ls,rs,v;}t[MAX<<5];
int rt[MAX],tot;
void Modify(int &x,int l,int r,int p)
{
    if(!x)x=++tot;t[x].v+=1;if(l==r)return;
    int mid=(l+r)>>1;
    if(p<=mid)Modify(t[x].ls,l,mid,p);
    else Modify(t[x].rs,mid+1,r,p);
}
ll tao1,tao2,ans;
void Merge(int &x,int y)
{
    if(!x||!y){x|=y;return;}
    t[x].v+=t[y].v;
    tao1+=1ll*t[t[x].ls].v*t[t[y].rs].v;
    tao2+=1ll*t[t[x].rs].v*t[t[y].ls].v;
    Merge(t[x].ls,t[y].ls);
    Merge(t[x].rs,t[y].rs);
}
int node=0,n;
int ch[MAX][2],V[MAX];
int gettree()
{
    int x=read(),u=++node;
    if(!x)ch[u][0]=gettree(),ch[u][1]=gettree();
    else V[u]=x;
    return u;
}
void dfs(int u)
{
    if(V[u]){Modify(rt[u],1,n,V[u]);return;}
    dfs(ch[u][0]);dfs(ch[u][1]);tao1=tao2=0;
    Merge(rt[u]=rt[ch[u][0]],rt[ch[u][1]]);
    ans+=min(tao1,tao2);
}
int main()
{
    n=read();gettree();
    dfs(1);
    printf("%lld\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/cjyyb/p/9889133.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值