P1908 逆序对

逆序对 - 洛谷

求逆序对的两种方法

1.归并排序

直接暴力查找时间复杂度为O(n2),但是如果查找第i个数和第1至i-1个数组成的逆序对,只要第1至i-1个数有序,所以只需要找到第一个比i大的数k,则第k到i-1都能与第i个数构成逆序对

所以我们可以在归并排序过程中查找

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Arrays;

class Main{
    static long res = 0;
    public static void main(String[] args) throws IOException {
        StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        cin.nextToken();
        int n = (int)cin.nval;
        int[] nums  = new int[n];
        for(int i=0;i<n;i++){
            cin.nextToken();
            nums[i] = (int)cin.nval;
        }
        merge_sort(nums,0,n-1);
        System.out.println(res);
    }
    public static void merge_sort(int[] nums,int l,int r){
        if(l>=r)
            return;
        int mid = (l+r)>>1;
        merge_sort(nums,l,mid);
        merge_sort(nums,mid+1,r);
        merge(nums,l,mid,r);
    }
    public static void merge(int[] nums,int l,int mid,int r){
        int[] arrl = Arrays.copyOfRange(nums,l,mid+1);
        int[] arrr = Arrays.copyOfRange(nums,mid+1,r+1);
        int i =0,j = 0;
        int k =l;
        while(i<arrl.length&&j<arrr.length){
            if(arrl[i]<=arrr[j]){
                nums[k++] = arrl[i++];
            }
            else{
                nums[k++] = arrr[j++];
                res+= mid-l-i+1;
            }
        }
        while(i<arrl.length){
            nums[k++] = arrl[i++];
        }
        while(j<arrr.length){
            nums[k++] = arrr[j++];
        }
    }
}

2.树状数组

对于数字x,我们在插入时记录他的插入次数i,所以我们在求以这个数为第二个数组成的逆序对的数量 = i - query(x),表示在这个数之前插入且大于这个数的数目

但是如果x过大,就会造成溢出,所以我们可以使用离散化处理

import java.util.Set;
import java.util.TreeSet;

public class lsh {
    public static int lower_bound(int[] arr,int x){             //在数组中找到第一个大于等于x的下标
        int low = 0,high = arr.length-1;
        int res = 0;
        while(low<=high){
            int mid = (low+high)>>1;
            if(arr[mid]>=x){
                res = mid;
                high = mid-1;
            }
            else
                low = mid+1;
        }
        return res;
    }
    public static void solve(int[] nums){
        Set<Integer> set = new TreeSet<>();
        for(int i:nums)
            set.add(i);
        int[] unique = new int[set.size()];
        int k = 0;
        for(int i:set)
            unique[k++] = i;
        for(int i=0;i<nums.length;i++){
            nums[i] = lower_bound(unique,nums[i])+1;
        }
    }
}

以下就是离散化后的树状数组代码

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.Set;
import java.util.TreeSet;

class Main{
    static int[] nums;
    static int[] tree;
    static int n;
    public static void main(String[] args) throws IOException {
        StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
        cin.nextToken();
        n = (int)cin.nval;
        nums = new int[n];
        tree = new int[n+1];
        long cnt = 0;
        for(int i=0;i<n;i++) {
            cin.nextToken();
            nums[i] = (int) cin.nval;
        }
        solve(nums);                //离散化
        for(int i=0;i<n;i++) {
            update(nums[i]);
            cnt += i-query(nums[i])+1;                  //在这个数前面插入且大于这个数总数 = 总插入数 - 在这个数前面插入且小于这个数总数 -1
        }
        System.out.println(cnt);
    }
    public static void update(int index){
        for(int i=index;i<=n;i+=(i&(-i))){
            tree[i]++;
        }
    }
    public static long query(int x){
        long sum=0;
        for(int i=x;i>0;i-=(i&(-i)))
            sum+=tree[i];
        return sum;
    }
    public static int lower_bound(int[] arr,int x){             //在数组中找到第一个大于等于x的下标
        int low = 0,high = arr.length-1;
        int res = 0;
        while(low<=high){
            int mid = (low+high)>>1;
            if(arr[mid]>=x){
                res = mid;
                high = mid-1;
            }
            else
                low = mid+1;
        }
        return res;
    }
    public static void solve(int[] nums){
        Set<Integer> set = new TreeSet<>();
        for(int i:nums)
            set.add(i);
        int[] unique = new int[set.size()];
        int k = 0;
        for(int i:set)
            unique[k++] = i;
        for(int i=0;i<nums.length;i++){
            nums[i] = lower_bound(unique,nums[i])+1;
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值