归并模板& P1908 逆序对

写法1,先开一个新的数组赋值。

#include<iostream>
#include<cstdio>
using namespace std;
int n,a[1001],c[1001];
void merge(int l,int r)
{
    if(l>=r) return;
    int mid=(l+r)/2;
    merge(l,mid);
    merge(mid+1,r);
    for(int i=l;i<=r;i++) c[i]=a[i];
    //c[l...mid],c[mid+1,r]
    int i=l,j=mid+1;
    for(int k=l;k<=r;k++)//a[k]要重新赋值 
    {
        if(i>mid) a[k]=c[j],j++;
        else if(j>r) a[k]=c[i],i++;
        else if(c[i]<c[j])
            a[k]=c[i],i++;
        else a[k]=c[j],j++;
    }
}
int main()
{
    cin>>n; 
    for(int i=1;i<=n;i++)
        cin>>a[i];
    merge(1,n);
    for(int i=1;i<=n;i++)
        cout<<a[i]<<" ";
}

写法2,后开一个新的数组赋值。
Once Self

#include<iostream>
#include<cstdio>
using namespace std;
int a[1001],c[1001];
void merge(int l,int r)
{
    if(l>=r) return;
    int mid=(l+r)/2;
    merge(l,mid);
    merge(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid && j<=r)
    {   
        if(a[i]<a[j]) 
            c[k++]=a[i],i++;
        else 
            c[k++]=a[j],j++;    
    }
    while(i>mid && k<=r) 
        c[k++]=a[j],j++;
    while(j>r && k<=r) 
        c[k++]=a[i],i++;
    for(int k=l;k<=r;k++) a[k]=c[k];//c[1] c[2] c[3] c[4] c[5] 
}
int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    merge(1,n);
    for(int i=1;i<=n;i++) cout<<a[i]<<" "; 
}

题目描述

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

输入输出格式

输入格式:
第一行,一个数n,表示序列中有n个数。

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

输出格式:
给定序列中逆序对的数目。

输入输出样例

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

/*6
12 3 1 2 45 3
out 7*/
说明
对于50%的数据,n≤2500
对于100%的数据,n≤40000。

2 6 7 8    1 3 4 5//<-正着想很麻烦,倒着想很简单
  i          j j j
#include<iostream>
#include<cstdio>
using namespace std;
int n,a[40005],c[40005],cnt;
void merge(int l,int r)
{
    if(l>=r) return;
    int mid=(l+r)/2;
    merge(l,mid);
    merge(mid+1,r);
    for(int i=l;i<=r;i++) c[i]=a[i];
    //c[l...mid],c[mid+1,r]
    int i=l,j=mid+1,t=0;
    for(int k=l;k<=r;k++)//
    {
        if(i>mid) a[k]=c[j],j++;
        else if(j>r) 
            a[k]=c[i],i++;
        else if(c[i]>c[j])
            a[k]=c[j],j++,cnt+=mid-i+1;//注意这的写法及顺序 (开始是相反的写法,很麻烦) 
        else a[k]=c[i],i++;
    }
}
int main()
{
    cin>>n; 
    for(int i=1;i<=n;i++)
        cin>>a[i];
    merge(1,n);
    cout<<cnt<<endl;
}
#include<iostream>
#include<cstdio>
using namespace std;
int a[40005],c[40005],ans;
void merge(int l,int r)
{
    if(l>=r) return;
    int mid=(l+r)/2;
    merge(l,mid);
    merge(mid+1,r);
    int i=l,j=mid+1,k=l;
    while(i<=mid && j<=r)
    {   
        if(a[i]>a[j]) 
        {
            ans+=j-k;//或者mid-i+1 
            c[k++]=a[j],j++;    
        }
        else 
            c[k++]=a[i],i++;    
    }
    while(i>mid && k<=r ) 
        c[k++]=a[j],j++;
    while(j>r && k<=r )
        c[k++]=a[i],i++;
    for(int k=l;k<=r;k++) a[k]=c[k];//c[1] c[2] c[3] c[4] c[5] 
}
int main()
{
    int n;cin>>n;
    for(int i=1;i<=n;i++)cin>>a[i];
    merge(1,n); 
    cout<<ans<<endl;
}

树状数组求逆序对

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,a[1005],b[1005],c[1005],f[1005],res;//*****
void add(int x,int y)
{
    while(x<=n)
    {
        f[x]+=y;
        x+=x&-x;//110+10->1000  0101+1->0110
    }   
} 
int query(int x)//查询前缀和 
{
    int sum=0;
    while(x)
    {
        sum+=f[x];
        x-=x&-x; 
    }
    return sum;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);b[i]=a[i];
    }
    sort(b+1,b+n+1);
    int u=unique(b+1,b+n+1)-(b+1);//去重 
    for(int i=1;i<=n;i++)
        c[i]=lower_bound(b+1,b+n+1,a[i])-b;//a[i]这个数在原序列中排第几

//  for(int i=1;i<=n;i++)
//      cout<<c[i]<<" ";
//      cout<<endl;

     //a[] 3 5 1 4 3 1-->9 
     //c[] 2 4 1 3 2 1
     //    1 2 3 4 
     //4进来时,求前缀和,那么3 5 1被加1,则前缀和是3 和5 组成的和 

    //然后读入一个,查询一下前缀和
    for(int i=1;i<=n;i++)
    {
//      cout<<"Q: "<<query(c[i])<<endl;
        res+=(i-1)-query(c[i]);
        add(c[i],1);
    } 
    cout<<res<<endl;
}
/*对于相同的数,比如1的前面有个1,但是它在算前面比它小的数时,确实0
 因此,在树状数组中应该包含它自己,将先add再query,改成先query再add,且query时要包含自己*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值