递归、分治 (牛客)

逆序数

 原题链接

题目描述

在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序。一个排列中逆序的总数就称为这个排列的逆序数。比如一个序列为4 5 1 3 2, 那么这个序列的逆序数为7,逆序对分别为(4, 1), (4, 3), (4, 2), (5, 1), (5, 3), (5, 2),(3, 2)。

输入描述:

第一行有一个整数n(1 <= n <= 100000),  然后第二行跟着n个整数,对于第i个数a[i],(0 <= a[i] <= 100000)。

输出描述:

输出这个序列中的逆序数

思路:用归并排序求逆序对 

ac代码:

#include<iostream>
#include<vector>
using namespace std;

#define ll long long
ll cnt=0;
const int N=1e6+10;
int a[N];

void merge(int l,int r,int mid){
    int l1=l,r1=mid+1;
    int k=0;
    vector<int> s(r-l+1);
    while(l1<=mid&&r1<=r){
        if(a[l1]<=a[r1]) 
        {
            s[k++]=a[l1++];
        }
        else{
            s[k++]=a[r1++];
            cnt+=mid-l1+1;
        }
    }
    while(l1<=mid){
        s[k++]=a[l1++];
    }
    while(r1<=r){
        s[k++]=a[r1++];
    }
    for(int i=l;i<=r;i++){
        a[i]=s[i-l];
    }
}
void merge_sort(int l,int r ){
    if(l>=r)
        return;
    int mid =(l+r)>>1;
    merge_sort(l,mid);
    merge_sort(mid+1,r);
    merge(l,r,mid);
}
int main(){
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>a[i];
    merge_sort(0,n-1);
    cout<<cnt<<endl;
    return 0;
}

 兔子的逆序对

原题链接

题目描述

兔子最近喜欢上了逆序对。
一个逆序对(i,j) 需要满足 i < j 且 ai > aj
兔子觉得只是求一个序列的逆序对个数太没有意思了。
于是兔子想到了一个更有趣的问题!
兔子可以把区间[L,R] 反转,例如序列{1,2,3,4} 反转区间[1,3] 后是{3,2,1,4}。
兔子有m次反转操作,现在兔子想知道每次反转后逆序对个数是奇数还是偶数,兔子喜欢偶数,而讨厌奇数。
请注意,每一次反转操作都会对原序列进行改变。例如序列{1,2,3,4} 第一次操作区间[1,2] 后变成{2,1,3,4} 第二次反转区间[3,4] 后变成 {2,1,4,3}

输入描述:

第一行一个整数 n,表示序列的大小。
第二行 n 个整数ai 表示序列的各个元素。
第三行一个整数m,表示操作个数。
接下来 m 行,每行两个整数 l,r,表示反转的区间。

输出描述:

输出共m行每行一个字符串,表示反转后序列逆序对个数的奇偶性,如果是逆序对个数奇数,输出"dislike"(不含引号),如果是偶数,输出"like"。

思路:先通过归并排序求出刚开始的逆序对数,因为交换一次序列中的两个数,其他数不变,则交换后的序列逆序对奇偶性交换。 反转区间[l,r],相当与前后指针依次交换,交换次数为长度 r-l 除以2.再判断交换次数的奇偶即可解决此问题。

代码:

#include<iostream>

using namespace std;

const int N=1e5+10;

int a[N];
int ans=0;
int w[N];

//归并排序求逆序对

void merge_sort(int l,int r){
    if(l>=r)  return ;
    int mid=(l+r)/2;
    merge_sort(l,mid);
    merge_sort(mid+1,r);
    int i=l,j=mid+1,cnt = l;
    while(i<=mid&&j<=r){
        if(a[i]<=a[j])
            w[cnt++]=a[i++];
        else{
            ans+=(mid-i+1);
            w[cnt++]=a[j++];
        }
    }
    while(i<=mid){
        w[cnt++]=a[i++];
    }
    while(j<=r){
        w[cnt++]=a[j++];
    }
    for(int i=l;i<=r;i++){
        a[i]=w[i];
    }
}



int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    
    int n,m;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    merge_sort(1, n);
    
    int l,r;
    cin>>m;
    while(m--){
        cin>>l>>r;
        int len=r-l+1;
        ans=(ans+len/2)%2;
        
        if(ans&1)
            cout<<"dislike\n";
        else
            cout<<"like\n";
    }
    return 0;
}

(新手上路,有错误希望大家指出。) 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值