POJ 2299 Ultra-QuickSort【求逆序数:归并排序|树状数组】
来源:互联网 发布:最新影视软件 编辑:程序博客网 时间:2024/05/01 10:13
题目点这
注意答案是long long型,然后注意相同数据的情况,如
3
1 1 1应当输出0
归并排序法【nlog(n)】求解:归并过程中如果a[indexA] > a[indexB] ,那么对于a[indexB]的逆序数有 mid - indexA + 1【包含自身】,如
下标: 0 1 2
a[indexA]:3 4 5
a[indexB]:2 1 3
那么对于a[indexB]里的2,有3个和它逆序的:3,4,5,合并之后2和345的逆序就为0了,因此不重复
//归并法求逆序数 3684K 375MS#include<stdio.h>#define ll long longll ans;int a[500001];int temp[500001];void Merge(int *arr,int first,int mid,int last){ int indexA = first; int indexB = mid+1; int k = 0; while(indexA <= mid && indexB <= last) { if(arr[indexA] <= arr[indexB]) { temp[k++] = arr[indexA]; indexA++; }else { temp[k++] = arr[indexB]; indexB++; ans += mid-indexA+1; //这里是核心 } } while(indexA <= mid) { temp[k++] = arr[indexA]; indexA++; } while(indexB <= last) { temp[k++] = arr[indexB]; indexB++; } int index = 0; for(int i=0;i<k;i++) { arr[first+index] = temp[i]; index++; }}void mergeSort(int *arr,int first,int last){ if(first < last) { int mid = (first+last)/2; mergeSort(arr,first,mid); mergeSort(arr,mid+1,last); Merge(arr,first,mid,last); }}int main(){ // freopen("in.txt","r",stdin); int n; while(scanf("%d",&n)!=EOF,n) { ans = 0L; for(int i=0;i<n;i++) { scanf("%d",&a[i]); } mergeSort(a,0,n-1); printf("%lld\n",ans); } return 0;}
精简版
#include <cstdio>#include <iostream>#include <algorithm>using namespace std;#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )#define LL long long#define RE freopen("1.in","r",stdin)#define WE freopen("1.out","w",stdout)#define NMAX 500002int tmp[NMAX],a[NMAX];LL ans;void Merge(int s[],int low,int mid,int high){ int i=low,j=mid+1,index=0; while(i<=mid&&j<=high) { if(s[i]<s[j]) tmp[index++]=s[i++]; else tmp[index++]=s[j++],ans+=mid-i+1; } while(i<=mid) tmp[index++]=s[i++]; while(j<=high) tmp[index++]=s[j++]; int pos=low; for(int w=0;w<index;w++) s[pos++]=tmp[w];}void Msort(int s[],int low,int high){ if(low<high) { int mid=(low+high)>>1; Msort(s,low,mid); Msort(s,mid+1,high); Merge(s,low,mid,high); }}int main(){ int n; //RE; while(cin>>n,n) { ans = 0; REP(i,n) cin>>a[i]; Msort(a,0,n-1); cout<<ans<<endl; }}
法② 树状数组+离散化【nlog(n)】:
需要离散化是因为a[i]最大是999,999,999,而树状数组里的c[i]像标记数组那样,针对本题下标可能到999,999,999,太大。所以把输入的a[i]根据下标映射到[1,500000]。
struct node
{
int pos;//原下标
int val;//原值
}in[NMAX];
pos保存输入数据的原下标,val保存原值,输入时pos = i;
降序排序后
for(int i=1;i<=n;i++)
{
r[in[i].pos] = i;
}
r数组保存离散后的情况
in数组排序后 r [ 排序后数据的下标 ] = i ;使 r [ 最大的那个数的下标] = 1,r[ 次大数的下标 ] = 2,……
r[]数组保存下标,
r[i]:原数据中,下标为i的数排第几,如果是升序就是第几小,降序就第几大
遍历r的话,等同于按排序前的顺序遍历原数据,r[i]为它的相对大小,也对应着排序前数据的下标,所以in[r[i]].val 为原值(根据排序后的大小关系)。
求逆序数只要保证有大小关系就不需要管数据具体大小,所以排序保证大小关系,之后的c数组也就跟数据大小无关了
pos:1 2 3 4
val:4 3 9999999 999
排序后
pos: 3 4 1 2
val: 9999999 999 4 3
-----离散Begin-----
r[3] = 1;
r[4] = 2;
r[1] = 3;
r[2] = 4;
-----离散End-----
然后对离散后的数组r顺序遍历,更新值+1
r[1] = 3; in[r[1]].val = in[3].val = 9999999
r[2] = 4; in[r[2]].val = in[4].val = 999
r[3] = 1; in[r[3]].val = in[1].val = 4
r[4] = 2; in[r[4]].val = in[2].val = 3
c[3] += 1...父节点+=1
c[4] += 1...父节点+=1
c[1] += 1...父节点+=1
c[2] += 1...父节点+=1
c[3]+=1表示最大值那个节点+1,然后c[4]:次大值那个节点+1
每插入一个数, 统计比他小的数的个数,对应的逆序为 i- getsum( r[i] ),
i 为当前已经插入的数的个数,
getsum( r[i] ) 为比 r[i] 小的数的个数,
i- sum( r[i] ) 即比 r[i] 大的个数, 即逆序的个数
再给个后来复习逆序数时加的例子:
输入数据:
val: 55 33 22 11 44
pos: 1 2 3 4 5
排序后:
i: 1 2 3 4 5
a[i].val: 11 22 33 44 55
a[i].pos: 4 3 2 5 1
r[1]=5:下标为1的数据排第5,即55是第5小,也就是第1大
r[5]=4:44是第4小
r[2]=3
r[3]=2
r[4]=1
顺序遍历:r[1]=5,r[2]=3,r[3]=2,r[4]=1,r[5]=4
c数组下标:1 2 3 4 5 getsum(r[1])=getsum(5)=1 i-getsum()=1-1=0
c[i]: 1
c数组下标:1 2 3 4 5 getsum(r[2])=getsum(3)=1 i-getsum()=2-1=1
c[i]: 1 1 1
c数组下标:1 2 3 4 5 getsum(r[3])=getsum(2)=1 i-getsum()=3-1=2
c[i]: 1 1 2 1
c数组下标:1 2 3 4 5 getsum(r[4])=getsum(1)=1 i-getsum()=4-1=3
c[i]: 1 2 1 3 1
c数组下标:1 2 3 4 5 getsum(r[5])=getsum(4)=4 i-getsum()=5-4=1
c[i]: 1 2 1 4 1
//树状数组+离散化求逆序数 7408K516MS#include<stdio.h>#include<algorithm>#include<string.h>using namespace std;#define ll long long#define NMAX 500001ll ans;struct node{ int pos;//原下标 int val;//原值}in[NMAX];int r[NMAX],c[NMAX];int n;int lowbit(int x){ return x&(-x);}void update(int x,int num){ while(x<=n) { c[x] += num; x += lowbit(x); }}int getSum(int x){ int sum=0; while(x>0) { sum += c[x]; x -= lowbit(x); } return sum;}int cmp(node a,node b){ return a.val < b.val;//升序}int main(){ //freopen("in.txt","r",stdin); while(scanf("%d",&n)!=EOF,n) { ans = 0L; for(int i=1;i<=n;i++) { scanf("%d",&in[i].val); in[i].pos = i; } sort(in+1,in+n+1,cmp); for(int i=1;i<=n;i++) { r[in[i].pos] = i; } memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) { update(r[i],1); ans += i - getSum(r[i]); } printf("%lld\n",ans); } return 0;}
- POJ 2299 Ultra-QuickSort 【归并排序求逆序数 OR 树状数组求逆序数】
- POJ 2299 Ultra-QuickSort【求逆序数:归并排序|树状数组】
- poj-2299-Ultra-QuickSort-归并排序求逆序数--或树状数组
- POJ Ultra-QuickSort 逆序数 树状数组 归并排序
- poj 2229 Ultra-QuickSort (归并排序求逆序数对|| 树状数组)
- poj 2299 Ultra-QuickSort(归并排序求逆序数)
- Poj 2299 Ultra-QuickSort(归并排序求逆序数)
- poj 2299 Ultra-QuickSort :归并排序求逆序数
- POJ 2299 Ultra-QuickSort(归并排序求逆序数)
- 2299 Poj Ultra-QuickSort(归并排序求逆序数)
- POJ 2299 Ultra-QuickSort (归并排序求逆序数)
- poj-2299 Ultra-QuickSort 归并排序求逆序数
- (POJ 2299)Ultra-QuickSort 归并排序求逆序数
- poj 2299 Ultra-QuickSort(求逆序数,树状数组)
- poj 2299 Ultra-QuickSort 求逆序数 树状数组解法
- Ultra-QuickSort poj 2299--树状数组求逆序数
- poj 2299 Ultra-QuickSort 树状数组求逆序数
- poj 2299 Ultra-QuickSort(树状数组 / 求逆序数)
- chrome找不到模拟移动端
- 断言(ASSERT)的用法
- 软键盘的关闭和显示
- window.location.search.substring(1); 什么意思
- Linux CRONTAB记录
- POJ 2299 Ultra-QuickSort【求逆序数:归并排序|树状数组】
- 26个省会城市平均工资 各地收入相同购买力不同
- 疑难杂症
- EXTJS 动态更新的折线图
- Access Violation
- ie6兼容性
- Codeforces Round#260E
- REST服务
- 腾讯提供的查询QQ在线状态