POJ 2299 Ultra-QuickSort 求逆序数 (归并或者数状数组)此题为树状数组入门题!!!
Ultra-QuickSort
Time Limit: 7000MS | Memory Limit: 65536K | |
Total Submissions: 70674 | Accepted: 26538 |
Description

Ultra-QuickSort produces the output
Your task is to determine how many swap operations Ultra-QuickSort needs to perform in order to sort a given input sequence.
Input
The input contains several test cases. Every test case begins with a line that contains a single integer n < 500,000 -- the length of the input sequence. Each of the the following n lines contains a single integer 0 ≤ a[i] ≤ 999,999,999, the i-th input sequence element. Input is terminated by a sequence of length n = 0. This sequence must not be processed.
Output
For every input sequence, your program prints a single line containing an integer number op, the minimum number of swap operations necessary to sort the given input sequence.
Sample Input
5 9 1 0 5 4 3 1 2 3 0
Sample Output
6 0
Source
题目意思:
对于给定的无序数组
求除经过多少次相邻的元素交换之后,可以使得数组升序
就是求一个数列的逆序数
对于给定的无序数组
求除经过多少次相邻的元素交换之后,可以使得数组升序
就是求一个数列的逆序数
方法一:
树状数组求解逆序数
从头到尾读入这些数,每读入一个数就更新树状数组,
查看它前面比它小的已出现过的有多少个数sum,
然后用当前位置减去该sum,
就可以得到当前数导致的逆序对数了。
把所有的加起来就是总的逆序对数。
查看它前面比它小的已出现过的有多少个数sum,
然后用当前位置减去该sum,
就可以得到当前数导致的逆序对数了。
把所有的加起来就是总的逆序对数。
code:
#include<queue> #include<set> #include<cstdio> #include <iostream> #include<algorithm> #include<cstring> #include<cmath> using namespace std; #define max_v 500005 int n; struct node { int v; int pos; } p[max_v]; int c[max_v]; int re[max_v]; int maxx; int lowbit(int x) { return x&(-x); } void update(int x,int d) { while(x<=n) { c[x]+=d; x+=lowbit(x); } } int getsum(int x) { int res=0; while(x>0) { res+=c[x]; x-=lowbit(x); } return res; } bool cmp(node a,node b) { return a.v<b.v; } int main() { while(~scanf("%d",&n)) { if(n==0) break; memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) { scanf("%d",&p[i].v); p[i].pos=i; } sort(p+1,p+1+n,cmp); for(int i=1;i<=n;i++) { re[p[i].pos]=i;//离散化 } long long ans=0; for(int i=1;i<=n;i++) { update(re[i],1); ans+=(i-getsum(re[i]));//当前位置减去前面比它小的数的个数之和就是答案 } printf("%lld\n",ans); } return 0; } /* 题目意思: 对于给定的无序数组 求除经过多少次相邻的元素交换之后,可以使得数组升序 就是求一个数列的逆序数 从头到尾读入这些数,每读入一个数就更新树状数组, 查看它前面比它小的已出现过的有多少个数sum, 然后用当前位置减去该sum, 就可以得到当前数导致的逆序对数了。 把所有的加起来就是总的逆序对数。 */
感觉自己的树状数组这样写真的是傻的一批,离散化写的麻烦了,照着别人的离散化写的!
现在明白了离散化是什么东西:就是数据范围压缩!!!
贴一个自己离散化的代码
#include<queue> #include<set> #include<cstdio> #include <iostream> #include<algorithm> #include<cstring> #include<cmath> using namespace std; #define max_v 500005 int n; struct node { int v; int pos; } p[max_v]; int c[max_v]; int re[max_v]; int maxx; int lowbit(int x) { return x&(-x); } void update(int x,int d) { while(x<max_v) { c[x]+=d; x+=lowbit(x); } } int getsum(int x)//返回1到x中小与等于x的数量 { int res=0; while(x>0) { res+=c[x]; x-=lowbit(x); } return res; } bool cmp(node a,node b) { if(a.v!=b.v) return a.v<b.v; else return a.pos<b.pos; } int main() { while(~scanf("%d",&n)) { if(n==0) break; memset(c,0,sizeof(c)); for(int i=1;i<=n;i++) { scanf("%d",&p[i].v); p[i].pos=i; } sort(p+1,p+1+n,cmp); long long ans=0; for(int i=1;i<=n;i++) { ans+=(i-getsum(p[i].pos)-1);//先找再更新,避免getsum的时候算上自己 update(p[i].pos,1); } printf("%lld\n",ans); } return 0; }
方法二:归并排序求解逆序数
在归并排序的过程中,比较关键的是通过递归,
将两个已经排好序的数组合并,
此时,若a[i] > a[j],则i到m之间的数都大于a[j],
合并时a[j]插到了a[i]之前,此时也就产生的m-i+1个逆序数,
而小于等于的情况并不会产生。
code:
#include<stdio.h> #include<memory> #define max_v 500005 typedef long long LL; LL a[max_v]; LL temp[max_v]; LL ans; void mer(int s,int m,int t) { int i=s; int j=m+1; int k=s; while(i<=m&&j<=t) { if(a[i]<=a[j]) { temp[k++]=a[i++]; }else { ans+=j-k;//求逆序数 temp[k++]=a[j++]; } } while(i<=m) { temp[k++]=a[i++]; } while(j<=t) { temp[k++]=a[j++]; } } void cop(int s,int t) { for(int i=s;i<=t;i++) a[i]=temp[i]; } int megsort(int s,int t) { if(s<t) { int m=(s+t)/2; megsort(s,m); megsort(m+1,t); mer(s,m,t); cop(s,t); } } int main() { int n; while(~scanf("%d",&n)) { if(n==0) break; ans=0; for(int i=0;i<n;i++) scanf("%lld",&a[i]); megsort(0,n-1); printf("%lld\n",ans); } return 0; } /* 题目意思: 对于给定的无序数组 求除经过多少次相邻的元素交换之后,可以使得数组升序 就是求一个数列的逆序数 在归并排序的过程中,比较关键的是通过递归, 将两个已经排好序的数组合并, 此时,若a[i] > a[j],则i到m之间的数都大于a[j], 合并时a[j]插到了a[i]之前,此时也就产生的m-i+1个逆序数, 而小于等于的情况并不会产生。 */
心之所向,素履以往
分类:
ACM
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南