P1908 逆序对

题目描述

猫猫TOM和小老鼠JERRY最近又较量上了,但是毕竟都是成年人,他们已经不喜欢再玩那种你追我赶的游戏,现在他们喜欢玩统计。最近,TOM老猫查阅到一个人类称之为“逆序对”的东西,这东西是这样定义的:对于给定的一段正整数序列,逆序对就是序列中ai>aj且i<j的有序对。知道这概念后,他们就比赛谁先算出给定的一段正整数序列中逆序对的数目。

输入输出格式

输入格式:

 

第一行,一个数n,表示序列中有n个数。

第二行n个数,表示给定的序列。

 

输出格式:

 

给定序列中逆序对的数目。

 

输入输出样例

输入样例#1: 
6
5 4 2 6 3 1
输出样例#1: 
11

说明

对于50%的数据,n≤2500

对于100%的数据,n≤40000。

 

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define date 100005
using namespace std;

int n;
long long ans;
int num[date];
int sum[date];

void merge_sort(int l,int r)
{
    if(l<r)
    {
        int mid=(l+r)/2;
        int x=l;
        int y=mid+1;
        int i=l;
        merge_sort(l,mid);
        merge_sort(mid+1,r);
        while(x<=mid||y<=r)
        {
            if(y>r||(x<=mid&&num[x]<=num[y]))
            {
                sum[i++]=num[x++];
            }
            else
            {
                sum[i++]=num[y++];
                ans+=mid-x+1;
            }
        }
        for(i=l;i<=r;i++)
        {
            num[i]=sum[i];
        }
    }
}

void init()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
    }
    merge_sort(1,n);
    cout<<ans;
}


int main()
{
    init();
    return 0;
}
归并排序
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

int n,ans,id[40005],c[40005];
struct NUM
{
    int num,id;
}num[40005];

bool cmp(NUM a,NUM b)
{
    return a.num>b.num;
}

int lowbit(int x)
{
    return x&(-x);
}

void add(int x)
{
    while(x<=n)
    {
        c[x]++;
        x+=lowbit(x);
    }
}

int sum(int x)
{
    int sum=0;
    while(x)
    {
        sum+=c[x];
        x-=lowbit(x);
    }
    return sum;
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i].num);
        num[i].id=i;    //离散化处理 
    }
    sort(num+1,num+n+1,cmp);
    for(int i=1;i<=n;i++)
    {
        id[num[i].id]=i;
    }
    for(int i=1;i<=n;i++)
    {
        ans+=sum(id[i]);
        add(id[i]);
    }
    printf("%d",ans);
    return 0;
}
BIT 树状数组
/*
    貌似是题解里唯一的一篇线段树题解。
    本来是要做动态逆序对的,但是我不会cdq分治,树状数组和分块不熟悉,所以想写线段树套线段树。
    然后就先来用线段树做一下不动态的。
    5倍空间消耗,比较慢,跑了近300ms 
    
    要离散化 
    指针+动态开点
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const int N=4e4+5;

int n,a,ans;
struct Num
{
    int num,id;
}num[N],aha[N];
struct Node
{
    Node *lson,*rson;
    int sum;
}node[N<<2];

typedef Node* Tree;
Tree now_node,root,null;

bool cmp1(Num a,Num b)
{
    return a.num<b.num;
}

bool cmp2(Num a,Num b)
{
    return a.id<b.id;
}

void init()
{
    node->lson=node->rson=node;
    now_node=null=root=node;
}

int read()
{
    char c=getchar();int num=0,f=1;
    for(;!isdigit(c);c=getchar());
    for(;isdigit(c);c=getchar())
        num=num*10+c-'0';
    return num;
}

Tree newNode()
{
    ++now_node;
    now_node->lson=now_node->rson=null;
    return now_node;
}

int query(Tree &root,int l,int r,int L,int R)
{
    if(root==null)
        return 0;
    if(L<=l&&r<=R)
        return root->sum;
    int mid=l+r>>1;
    int ret=0;
    if(L<=mid)
        ret+=query(root->lson,l,mid,L,R);
    if(mid<R)
        ret+=query(root->rson,mid+1,r,L,R);
    return ret;
}

void modify(Tree &root,int l,int r,int pos)
{
    if(root==null)
        root=newNode();
    if(l==r)
    {
        root->sum=1;
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid)
        modify(root->lson,l,mid,pos);
    else
        modify(root->rson,mid+1,r,pos);
    root->sum=root->lson->sum+root->rson->sum;
}

int main()
{
    //freopen("testdata.in","r",stdin);
    init();
    n=read();
    for(int i=1;i<=n;++i)
    {
        num[i].num=read();
        num[i].id=i;
        aha[i].id=i;
    }
    sort(num+1,num+n+1,cmp1);
    for(int i=1;i<=n;++i)    //离散化,按大小编新的编号 
        aha[num[i].id].num=i;
    //sort(aha+1,aha+n+1,cmp2);    //按输入顺序排序,还原原序列 
    for(int i=1;i<=n;++i)
    {
        ans+=query(root,1,n,aha[i].num,n);    //查找在它之前的比它大的数 
        modify(root,1,n,aha[i].num);    //标记一下这个数已经出现 
    }
    printf("%d",ans);
    return 0;
}
线段树

 

posted @ 2018-02-23 21:22  whymhe  阅读(313)  评论(0编辑  收藏  举报