逆序对相关专题

逆序对相关的题目:

题目1: 逆序对模板题: https://www.acwing.com/problem/content/description/790/

题目2: 百练平台的重要逆序对:http://bailian.openjudge.cn/xly2018/E/

逆序对问题的处理方法:

1.(推荐): 分治法,归并排序的处理思路

2. 哈希+树状数组

注意: 个人比较推荐第一种逆序对的做法,比较具有普适性. 哈希+树状数组的做法在遇到一些特定的题目(e.g. 题目2)的时候会相对比较难以处理.

主要来讲一讲"分治法"

分治法的主要思路就是讲规模为n的问题拆分为左半部分和右半部分, 递归处理这两部分. 然后对这两部分进行合并(归并). 应用到逆序对中, 我们可以将整个序列的逆序对划分为以下3类:

1. 左半部分的逆序对

2. 右半部分的逆序对

3. 横跨两边的逆序对

其中1和2可以由递归函数处理得到,只需要考虑3如何统计.

常规的暴力统计的方法去统计3,复杂度是O(n^2). 复杂度高的原因: 没有充分利用序列信息.

注意到: 3是统计横跨两边的逆序对,与左半部分序列的内部顺序无关,右半部分同理.

事实上,我们可以规定我们的递归函数的副作用为: 对于一个序列,先做逆序对的统计,然后再将这个序列排序.

那么在递归处理左半部分和右半部分之后,我们得到的左半部分和右半部分的序列都是有序的.

然后我们用双指针统计横跨两边的逆序对即可. 这样的时间复杂度是O(n)的.

因此,总的时间复杂度就是O(nlogn).

这种逆序对的处理方法,是具有一定的普适性的,对于题目2,只需要将双指针的移动条件进行相应的修改即可.

题目1的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+10;
int a[N];
int b[N],c[N];
int n;

ll  merge(int l,int r){
    // the merge opts to 1. calculate the number of inverse pair
    // 2. to sort the [l, r] of the array.
    if(l>r||l==r) return 0;
    int mid=l+r>>1;
    ll l_inverse=merge(l,mid);
    ll r_inverse=merge(mid+1,r);
    int lp=l,rp=mid+1;
    ll res=0;
    res=l_inverse+r_inverse;
    while(lp<=mid){
        while(rp<=r&&a[rp]<a[lp]) rp++;
        res+=(rp-1)-(mid+1)+1;
        
        lp++;
    }
    lp=l,rp=mid+1;
    int cnt=l;
    while(lp<=mid&&rp<=r){
        if(a[lp]<a[rp]) b[cnt++]=a[lp++];
        else b[cnt++]=a[rp++];
    }
    while(lp<=mid) b[cnt++]=a[lp++];
    while(rp<=r) b[cnt++]=a[rp++];
    for(int i=l;i<=r;i++) a[i]=b[i];
    return res;
}

int main(void){
    
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    
    ll res=merge(1,n);
    printf("%lld",res);
    return 0;
}

题目2的代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+10;
int a[N];
int b[N],c[N];
int n;

ll  merge(int l,int r){
	// the merge opts to 1. calculate the number of inverse pair
	// 2. to sort the [l, r] of the array.
	if(l>r||l==r) return 0;
	int mid=l+r>>1;
	ll l_inverse=merge(l,mid);
	ll r_inverse=merge(mid+1,r);
	int lp=l,rp=mid+1;
	ll res=0;
	res=l_inverse+r_inverse;
	while(lp<=mid){
		while(rp<=r&&2*a[rp]<a[lp]) rp++;
		res+=(rp-1)-(mid+1)+1;
		
		lp++;
	}
	lp=l,rp=mid+1;
	int cnt=l;
	while(lp<=mid&&rp<=r){
		if(a[lp]<a[rp]) b[cnt++]=a[lp++];
		else b[cnt++]=a[rp++];
	}
	while(lp<=mid) b[cnt++]=a[lp++];
	while(rp<=r) b[cnt++]=a[rp++];
	for(int i=l;i<=r;i++) a[i]=b[i];
	return res;
}

int main(void){
	
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	
	ll res=merge(1,n);
	printf("%lld",res);
	return 0;
}

### 回答1: 题目描述: 给定一个整数n和n个整数,将这n个整数按照逆序输出。 输入格式: 第一行输入一个整数n,表示整数的个数。 接下来一行输入n个整数,用空格隔开。 输出格式: 按照逆序输出n个整数,每个整数占一行。 样例输入: 5 1 2 3 4 5 样例输出: 5 4 3 2 1 解题思路: 本题需要将输入的n个整数按照逆序输出,可以使用数组来存储这n个整数,然后从数组的最后一个元素开始遍历,依次输出每个元素即可。 具体实现可以定义一个长度为n的数组,然后使用for循环从输入中读取n个整数,存储到数组中。接着再使用for循环从数组的最后一个元素开始遍历,依次输出每个元素即可。 参考代码: ### 回答2: 对于这个问题,我们需要编写一个函数,可以实现将多个整数按逆序输出。首先,我们可以将这些整数保存在一个列表中。 接下来,我们使用一个循环来遍历列表,从最后一个元素开始依次输出。具体步骤如下: 1. 我们定义一个函数,比如说叫做reverse_print,这个函数接受一个列表作为参数。 2. 在函数内部,我们使用一个for循环来遍历这个列表。循环的范围是从列表的最后一个元素到第一个元素,步长为-1。 3. 在循环中,我们使用print函数来输出每一个元素。由于题目要求逆序输出,所以我们使用列表的索引来访问元素。具体来说,我们可以使用负数索引,比如说-1表示最后一个元素,-2表示倒数第二个元素,以此类推。 4. 循环结束后,我们通过在函数外部调用这个函数并传入一个整数列表来测试代码。比如说,我们可以调用reverse_print([1, 2, 3, 4, 5])。这样,函数就会将这个列表中的整数逆序输出。 通过以上步骤,我们就完成了这个函数的编写。这个函数可以接收一个整数列表作为参数,并将其中的整数逆序输出。 ### 回答3: 题目中的1111代表的是一个函数题目的编号,也就是多个整数的逆序输出。该题目要求编写一个函数,输入一串整数,输出这串整数逆序排列的结果。 为了实现这个功能,我们可以采用以下步骤: 1. 首先,我们需要定义一个函数,命名为reverse_output。 2. 在函数中,我们需要接收一个参数,即需要逆序输出的整数串。 3. 我们可以使用split()函数将整数串按照空格分隔成一个整数列表。 4. 接下来,我们可以使用reverse()函数将整数列表进行逆序操作。 5. 最后,我们可以使用join()函数将逆序后的整数列表转换为一个字符串,并使用空格将其中的整数分隔开。 6. 最后,在函数中使用print()函数将逆序后的整数字符串输出。 以下是一种可能的实现方式: ``` def reverse_output(nums): num_list = nums.split() num_list.reverse() reverse_nums = " ".join(num_list) print(reverse_nums) ``` 我们可以测试一下这个函数的功能: ``` reverse_output("1 2 3 4 5") # 输出:5 4 3 2 1 reverse_output("10 20 30 40 50") # 输出:50 40 30 20 10 ``` 通过这个函数,我们可以将输入的整数串逆序输出,实现题目要求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值