[BZOJ2212][Poi2011]Tree Rotations(线段树合并)

题目描述

传送门

题解

好像很久以前有一个神人写过什么证明说n个logn的链(线段树上)合并起来复杂度是nlogn的?
那么空间复杂度不会超过时间复杂度?

总逆序对数=左子树逆序对数+右子树逆序对数+左子树对右子树的影响
分别计算交换之前交换之后的然后再判断换不换
计算某一个子树对另一个子树的影响与这两个子树内部的顺序无关
如何计算一个子树对另一个子树的影响?

分别对这两个子树建立权值线段树
将这两个权值线段树合并
合并的过程中累加右子树(l,mid)*左子树(mid+1,r)即为影响
这样自底向上合并就可以了

正确性?
显然。。。
相当于每一次划分一个分界点,统计跨越这个分界点的贡献
第一次做这样的题,涨姿势

代码

#include
#include
#include
#include
#include
using namespace std;
#define LL long long
#define N 400005

int n,r,cnt,sz;
int ch[N][2],val[N];
int root[N],ls[N*30],rs[N*30];
LL sum[N*30];
LL ans,ansl,ansr;

void read(int &now)
{
    now=++cnt;
    scanf("%d",&val[now]);
    if (!val[now])
    {
        read(ch[now][0]);
        read(ch[now][1]);
    }
}
void update(int now)
{
    sum[now]=sum[ls[now]]+sum[rs[now]];
}
void insert(int &now,int l,int r,int x)
{
    int mid=(l+r)>>1;
    now=++sz;
    if (l==r)
    {
        sum[now]=1;
        return;
    }
    if (x<=mid) insert(ls[now],l,mid,x);
    else insert(rs[now],mid+1,r,x);
    update(now);
}
int merge(int x,int y)
{
    if (!x) return y;
    if (!y) return x;
    ansl+=sum[rs[x]]*sum[ls[y]];
    ansr+=sum[ls[x]]*sum[rs[y]];
    ls[x]=merge(ls[x],ls[y]);
    rs[x]=merge(rs[x],rs[y]);
    update(x);
    return x;
}
LL dfs(int x)
{
    LL ans=0;
    if (!val[x])
    {
        ans=dfs(ch[x][0])+dfs(ch[x][1]);
        ansl=ansr=0;
        root[x]=merge(root[ch[x][0]],root[ch[x][1]]);
        ans+=min(ansl,ansr);
    }
    else insert(root[x],1,n,val[x]);
    return ans;
}

int main()
{
    scanf("%d",&n);
    read(r);
    ans=dfs(1);
    printf("%lld\n",ans);
}


你可能感兴趣的:(题解,线段树)