poj 2299 逆序数

http://poj.org/problem?id=2299

坑:答案是long long 输出……!!!!!

题意是:求一个数组进行冒泡排序交换的次数

题解:求逆序数

题解Ⅰ:

归并排序求逆序数

归并排序求逆序数之前写过

1.归并排序是把两个有序的数组合并成为一个有序的数组,利用分治的思想就可以进行排序

  逆序数可以利用这个思想求

  求出第一个数组的逆序数,和第二个数组的逆序数,再将两个数组整体的逆序数求出来

  f(x,y) = f(x,mid) + f(mid,y) + 之后数组的逆序数

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <vector>
#include <map>
#include <queue>
#include <stack>
const int MAXN =  500000 + 10;
const int INF = 0x7fffffff;
const int MOD = 1000007;
const double ESP = 10e-8;
const double Pi = acos(-1.0);
typedef long long LL;
using namespace std;
LL a[MAXN];
LL b[MAXN];
LL h(int s,int e){
    if(e-s <= 1){
        return 0;
    }
    int mid = s + (e-s)/2;
    LL x = h(s,mid);
    LL y = h(mid,e);
    int p1 = s;
    int p2 = mid;
    int p3 = s;
    LL cnt = 0;
    while(p1 < mid || p2 < e){
        if(p2 >= e || (p1 < mid && a[p1] < a[p2])){
            b[p3++] = a[p1++];
        }
        else{
            b[p3++] = a[p2++];
            cnt += (mid-p1); /*第二个数组当前元素比第一个数组当前元素小,所以第一个数组从当前元素到最后的元素都比第二个数组的大(数组一,二都已经有序了),所以第一个数组结尾下标,减去第一个数组的当前元素就是两个数组的逆序数*/
        }
    }
    for(int i = s;i < e;i++){
        a[i] = b[i];
    }
    return x+y+cnt;
}
int main(){
    //freopen("input.txt","r",stdin);
    int n;
    while(~scanf("%d",&n) && n){
        for(int i = 0;i < n;i++){
            scanf("%d",&a[i]);
        }
        LL ans = h(0,n);
        printf("%lld\n",ans);
    }
    return 0;
}

题解Ⅱ:

http://www.cnblogs.com/shenshuyang/archive/2012/07/14/2591859.html

这个童鞋写得已经很棒了
1.数组元素太大,而n 的个数又很少,所以需要离散化

离散化:

用struct Node{

  int v;

  int order;

};

将元素读入,并且将元素的次序读入

将元素排序之后,下标的标号一定程度上是代表元素的大小

所以用下标的标号就可以表示元素

这样范围就减少了

2.

树状数组是求 前 i 个 元素的和

先 add(a[i],1)

再 i - sum(a[i])

前面已经有 i 个元素了,sum(a[i]) 表示 1 - a[i] 的和 ,而 i - sum(a[i])  表示有多少个数字比 a[i] 大 但是 次序却在 i 位置的前面  就是 a[i] 元素的逆序数

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstdlib>
#include <vector>
#include <map>
#include <queue>
#include <stack>
const int MAXN =  500000 + 10;
const int INF = 0x7fffffff;
const int MOD = 1000007;
const double ESP = 10e-8;
const double Pi = acos(-1.0);
typedef long long LL;
using namespace std;
int a[MAXN];
int bit[MAXN+1];
int n;
struct Node{
    int v;
    int order;
    bool operator < (const Node x)const{
        return v < x.v;
    }
};
Node in[MAXN];
int sum(int i){
    int s = 0;
    while(i>0){
        s += bit[i];
        i -= (i & -i);
    }
    return s;
}
void add(int i,int x){
    while(i <= n){
        bit[i] += x;
        i += (i&-i);
    }
}
int main(){
//    freopen("input.txt","r",stdin);
    while(~scanf("%d",&n) && n){
        memset(bit,0,sizeof(bit));
        for(int i = 1;i <= n;i++){
            scanf("%d",&in[i].v);
            in[i].order = i;
        }
        sort(in+1,in+1+n);
        for(int i = 1;i <= n;i++){
            a[in[i].order] = i;
        }
        LL ans = 0;
        for(int i = 1;i <= n;i++){
            add(a[i],1);
            ans += i-sum(a[i]);
        }
        printf("%lld\n",ans);
    }
    return 0;
}
时间: 2024-10-03 07:17:14

poj 2299 逆序数的相关文章

POJ 2299 逆序对(归并排序)

终于解决了一个忧伤好久的问题,严重拖了项目进度,深感惭愧!一直被一系列的问题所困扰,然后又只能自己一个人摸索,也是一段辛酸忧伤史,现在小结一下上个月在做二维码的过程中所碰到的问题以及解决办法,现在庆幸终于解决好了,终于能将这个功能告一段落,一下小结也是分享一下Unity的某些"坑",让同行少走弯路,起码在二维码这方面应该会有所启迪,欣慰的是接下来几天终于可以做自己应该做的事情了! 效果图: 先小结一下碰到的问题: 1.Unity工程屏幕方向与Android工程屏幕方向要一致的问题 本来

POJ 2299 逆序对

Crossings Time Limit: 2 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100463 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent

poj 2299 Ultra-QuickSort 归并排序求逆序数对

题目链接: http://poj.org/problem?id=2299 题目描述: 给一个有n(n<=500000)个数的杂乱序列,问:如果用冒泡排序,把这n个数排成升序,需要交换几次? 解题思路: 根据冒泡排序的特点,我们可知,本题只需要统计每一个数的逆序数(如果有i<j,存在a[i] > a[j],则称a[i]与 a[j]为逆序数对),输出所有的数的逆序数的和用普通排序一定会超时,但是比较快的排序,像快排又无法统计 交换次数,这里就很好地体现了归并排序的优点.典型的利用归并排序求逆

poj 2299 Ultra-QuickSort(树状数组求逆序数+离散化)

题目链接:http://poj.org/problem?id=2299 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swapping two adjacent sequence elements until the sequence is sorted in

poj 2299 Ultra-QuickSort (归并排序,逆序数)

链接:poj 2299 题意:给出长度为n的序列,每次只能交换相邻的两个元素, 问至少要交换几次才使得该序列为递增序列 分析:冒泡排序每次只能交换相邻两个元素,也就是求 用冒泡排序使其为递增序列的交换次数,每交换一次记录一次就好 但是这题数据较大,冒泡排序效率比较低,会超时的 这里就可以利用归并排序了,用归并排序可以求序列的逆序数, 而一个序列的 逆序数 = 只允许相邻两个元素交换时,得到有序序列的交换次数 #include<stdio.h> #include<stdlib.h>

poj 2299 树状数组求逆序数+离散化

http://poj.org/problem?id=2299 最初做离散化的时候没太确定但是写完发现对的---因为后缀数组学的时候,,这种思维习惯了吧 1.初始化as[i]=i:对as数组按照num[]的大小间接排序 2.bs[as[i]]=i:现在bs数组就是num[]数组的离散化后的结果 3.注意,树状数组中lowbit(i)  i是不可以为0的,0&(-0)=0,死循环... #include <cstdio> #include <cstring> #include

POJ 2299 Ultra-QuickSort (归并排序求逆序数)

Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 47235   Accepted: 17258 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swappin

逆序数 POJ 2299 Ultra-QuickSort

题目传送门 1 /* 2 暴力 超时 3 */ 4 #include <stdio.h> 5 6 const int MAX_N = 500000; 7 int a[MAX_N+10]; 8 long long cnt = 0; 9 10 int main(void) 11 { 12 //freopen ("inD.txt", "r", stdin); 13 int n; 14 15 while (scanf ("%d", &

归并排序求逆序数(POJ 1804,POJ 2299,HDU 4911)

首先,明确两个概念: 逆序对:数列a[1],a[2],a[3]-中的任意两个数a[i],a[j] (i<j),如果a[i]>a[j],那么我们就说这两个数构成了一个逆序对. 逆序数:一个数列中逆序对的总数. 例题一:POJ 1804.   点击打开链接 解题思路:每次交换只能减少一个逆序,而且必定能减少一个逆序,从而问题就转换为求逆序个数了.这题数据规模很小,暴力可过. 我这里提供了用Merge_sort的方法求解逆序数,时间复杂度为O(nlogn). 关于归并排序:归并排序是将数列a[l,h