逆序数 - 一维 - 平衡树

1.逆序数:

求逆序数

时间限制: 2000 ms  |  内存限制: 65535 KB
难度: 5
描述

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。

现在,给你一个N个元素的序列,请你判断出它的逆序数是多少。

比如 1 3 2 的逆序数就是1。

输入
第一行输入一个整数T表示测试数据的组数(1<=T<=5)
每组测试数据的每一行是一个整数N表示数列中共有N个元素(2〈=N〈=1000000)
随后的一行共有N个整数Ai(0<=Ai<1000000000),表示数列中的所有元素。

数据保证在多组测试数据中,多于10万个数的测试数据最多只有一组。
输出
输出该数列的逆序数
样例输入
2
2
1 1
3
1 3 2
样例输出
0
1


算法1:

归并:

逆序数为sum

l [n]和r [m]为已经排好的

则如果l [i] > r [j]

则sum+=(n-i);


#include <iostream>
#include <string.h>

using namespace std;
#define maxn 1000010
int s[maxn];
int ll[maxn/2],rr[maxn/2];
int sum=0;

void mg(int l,int m,int r){
    int i,j,k=0;
    for(i=l;i<=m;i++){
        ll[k++]=s[i];
    }k=0;
    for(i=m+1;i<=r;i++){
        rr[k++]=s[i];
    }
    int a,b;
    a=m-l+1;b=r-m;
    k=l;
    for(i=0,j=0;i<a && j<b;){
        if(ll[i]<=rr[j]){
            s[k++]=ll[i];i++;
        }else{
            s[k++]=rr[j];j++;
            sum+=(a-i);
        }
    }
    while(i<a){
        s[k++]=ll[i++];
    }
    while(j<b){
        s[k++]=rr[j++];
    }
}

void mf(int l,int r){
    if(l<r){
        int m=(l+r)/2;
        mf(l,m);
        mf(m+1,r);
        mg(l,m,r);
    }
}

int main(){
    int nc;
    cin>>nc;
    while(nc--){
        int n;
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>s[i];
        }
        mf(0,n-1);
        cout<<sum<<endl;
    }

return 0;}

算法2:

树状数组:

 
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 1000000
long long c[N+1],aaa[N+1];
struct sb
{
	int x;
	int y;
}yi[N+1];
bool cmp(struct sb t1,struct sb t2)
{
    if(t1.x!=t2.x)
	   return t1.x<t2.x;
    else
       return t1.y<t2.y;
}
int lowbit (int a)
{
	return a&(-a);
}
void add(int a)
{
	while(a<N+1)
	{
		c[a]+=1;
		a+=lowbit(a);
	}
}
int Sum(int a)
{
	int SUM=0;
	while(a>0)
	{
		SUM+=c[a];
		a-=lowbit(a);
	}
	return SUM;
}
int main()
{
	int a,b,n,m;
	scanf("%d",&n);
	while(n--)
	{
		long long sum=0;
		memset(c,0,sizeof(c));
		scanf("%d",&m);
		for(a=1;a<=m;a++)
		{
			scanf("%lld",&yi[a].x);
			yi[a].y=a;
		}
        std::sort(yi+1,yi+m+1,cmp);//离散化。。。
		for(a=1;a<=m;a++)
			aaa[yi[a].y]=a; 
		for(a=1;a<=m;a++)
		{
		    add(aaa[a]);
			sum+=(a-1-Sum(aaa[a]-1));

		}
		printf("%lld\n",sum);     

	}
}
        



 算法训练 逆序对  
时间限制:1.0s   内存限制:256.0MB
锦囊1
使用平衡树。
锦囊2
从叶子到根依次处理,每次把结点个数少的树中的结点依次添加到结点个数多的里面,并顺便计算逆序对个数和两棵子树交换后的逆序对个数。
问题描述

Alice是一个让人非常愉跃的人!他总是去学习一些他不懂的问题,然后再想出许多稀奇古怪的题目。这几天,Alice又沉浸在逆序对的快乐当中,他已近学会了如何求逆序对对数,动态维护逆序对对数等等题目,他认为把这些题让你做简直是太没追求了,于是,经过一天的思考和完善,Alice终于拿出了一道他认为差不多的题目:

有一颗2n-1个节点的二叉树,它有恰好n个叶子节点,每个节点上写了一个整数。如果将这棵树的所有叶子节点上的数从左到右写下来,便得到一个序列a[1]…a[n]。现在想让这个序列中的逆序对数量最少,但唯一的操作就是选树上一个非叶子节点,将它的左右两颗子树交换。他可以做任意多次这个操作。求在最优方案下,该序列的逆序对数最少有多少。

Alice自己已近想出了题目的正解,他打算拿来和你分享,他要求你在最短的时间内完成。

输入格式

第一行一个整数n。

下面每行,一个数x。

如果x=0,表示这个节点非叶子节点,递归地向下读入其左孩子和右孩子的信息,如果x≠0,表示这个节点是叶子节点,权值为x。

输出格式
输出一个整数,表示最少有多少逆序对。
样例输入
3
0
0
3
1
2
样例输出
1
数据规模与约定

对于20%的数据,n <= 5000。

对于100%的数据,1 <= n <= 200000,0 <= a[i]<2^31。



代码:--摘自网络


#include<stdio.h>
#define N 200010
long long ans = 0;
int left[N], right[N];
int len[N];
int vals[N];
int vTop = 1;
int lRotate(int rt)
{
    int nRt = right[rt];
    right[rt] = left[nRt];
    left[nRt] = rt;

    len[nRt] = len[rt];
    len[rt] = len[left[rt]] + len[right[rt]] + 1;
    return nRt;
}
int rRotate(int rt)
{
    int nRt = left[rt];
    left[rt] = right[nRt];
    right[nRt] = rt;

    len[nRt] = len[rt];
    len[rt] = len[left[rt]] + len[right[rt]] + 1;
    return nRt;
}
int adjust(int rt, int isLeft)
{
    if(isLeft)
    {
        if(len[left[left[rt]]] > len[right[rt]] || len[right[left[rt]]] > len[right[rt]])
        {
            if(len[right[left[rt]]] > len[right[rt]])
            {
                left[rt] = lRotate(left[rt]);
            }
            return rRotate(rt);
        }
    }
    else
    {
        if(len[left[right[rt]]] > len[left[rt]] || len[right[right[rt]]] > len[left[rt]])
        {
            if(len[left[right[rt]]] > len[left[rt]])
            {
                right[rt] = rRotate(right[rt]);
            }
            return lRotate(rt);
        }
    }
    return rt;
}
int insert(int rt, int node)
{
    len[rt]++;
    if(vals[node] < vals[rt])
    {
        if(left[rt] == 0)
        {
            left[rt] = node;
        }
        else
        {
            left[rt] = insert(left[rt], node);
        }
    }
    else
    {
        if(right[rt] == 0)
        {
            right[rt] = node;
        }
        else
        {
            right[rt] = insert(right[rt], node);
        }
    }
    return adjust(rt, vals[node] < vals[rt]);
}
int rank(int rt, int val)
{
    if(rt == 0)
    {
        return 0;
    }
    else if(val >= vals[rt])
    {
        return rank(right[rt], val);
    }
    else
    {
        return rank(left[rt], val) + 1 + len[right[rt]];
    }
}
int merge(int des, int vBegin, int vEnd)
{
    long long ca = 0, cb = 0;
    int i;
    for(i = vBegin; i < vEnd; i++)
    {
        ca += rank(des, vals[i]);
        cb += len[des] - rank(des, vals[i] - 1);
    }
    ans += ca < cb ? ca : cb;
    for(i = vBegin; i < vEnd; i++)
    {
        left[i] = right[i] = 0;
        len[i] = 1;
        des = insert(des, i);
    }
    return des;
}
int buildTree()
{
    int val;
    scanf("%d", &val);
    if(val != 0)
    {
        left[vTop] = right[vTop] = 0;
        len[vTop] = 1;
        vals[vTop] = val;
        return vTop++;
    }
    int ls = vTop;
    int rlt = buildTree();
    int rs = vTop;
    int rrt = buildTree();
    int re = vTop;
    if(rs - ls > re - rs)
    {
        return merge(rlt, rs, re);
    }
    else
    {
        return merge(rrt, ls, rs);
    }
}
int main(void)
{
    int n;
    scanf("%d", &n);
    buildTree();
    printf("%I64d", ans);
    return 0;
}









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值