Description
Input
Output
Sample Input
5 9 1 0 5 4 3 1 2 3 0
Sample Output
6 0
Source
搁浅了好久了树状数组,尤其是在两天都被操作系统虐的不行不行的情况下,捡起本来就不咋熟练的这货还是有些费事。
这次的专题没做裸的树状数组,逆序数算是树状数组里面比较典型的应用了,尤其是500,000的数据量需要用到离散化,啥是离散化?
以这个题为例,手写了一下中间变量的输出:原数组是9,1,0,5,4 离散后变成了5,2,1,4,3大大缩减了空间开销,这里需要着重注意一下~~
再就说道求逆序数的原理:查询函数返回在序列中比这个数小的个数,假定序列中第i个数为a,那么前i个数中比i大的元素个数为i-sum(i)。当然了,求之前需要调用add函数,函数的第一个参数本来是位置,在这个题中当然带进去的也是离散后的相对位置,貌似说的不是特别明白==
用最原始的树状数组来说明,数组A代表数字i在序列中是否出现过,如果已经存在,那么A【i】=1,否则等于0,,此时查询函数返回值为序列中比数字i小的个数。因为是一位一位add进去,增加一个查询一个,出现了一个数x,则a[x]=1,暂时没出现的就是0,查询的是当前这个数以前的,那么答案显而易见啦
/*************** 2016.2.1 poj2277 8152K 391MS G++ 1129B ***************/ #include <iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 500005 int n; int c[maxn],b[maxn]; struct Node { int index,v; }node[maxn]; bool cmp(Node a,Node b) { return a.v<b.v; } int lowbit(int x) { return x&(-x); } void add(int i,int val) { while(i<=n) { c[i]+=val; i+=lowbit(i); } } int sum(int i) { int s=0; while(i>0) { s+=c[i]; i-=lowbit(i); } return s; } int main() { //freopen("cin.txt","r",stdin); while(~scanf("%d",&n)&&n) { for(int i=1;i<=n;i++) { scanf("%d",&node[i].v); node[i].index=i; } memset(b,0,sizeof(b)); memset(c,0,sizeof(c)); sort(node+1,node+1+n,cmp); b[node[1].index]=1; for(int i=2;i<=n;i++) { b[node[i].index]=i; } long long ans=0; for(int i=1;i<=n;i++) { add(b[i],1); ans+=(i-sum(b[i])); } printf("%I64d\n",ans); } return 0; }
不是自己的代码,但是是裸的应用,没必要再写一遍是吧
#include <stdio.h> #include<string.h> #include<iostream> using namespace std; const int MAXN=500010; long long ans;//存放逆序数,比较大,必须用long long int a[MAXN],b[MAXN],c[MAXN]; //将已经排好序的left~mid,mid+1~right进行归并 void merge(int *a,int left,int mid,int right) { int i,j; i=0; for(j=left;j<=mid;j++) b[i++]=a[j]; int len1=mid-left+1; i=0; for(j=mid+1;j<=right;j++) c[i++]=a[j]; int len2=right-mid; i=0; j=0; int k=left; while(i<len1&&j<len2&&k<=right) { if(b[i]<=c[j]) { a[k++]=b[i++]; } else { a[k++]=c[j++]; ans+=(len1-i);//逆序数就是累加后面比自己小的数的个数 //此时b[i]>c[j],那么c[j]会给b[i]后面的len1-i个数造成逆序数 } } while(i<len1) a[k++]=b[i++]; while(j<len2) a[k++]=c[j++]; } void merge_sort(int *a,int left,int right)//对a[left~right-1]进行归并排序 { if(left<right) { int mid=(left+right)/2; merge_sort(a,left,mid); merge_sort(a,mid+1,right); merge(a,left,mid,right); } } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n; while(scanf("%d",&n),n) { for(int i=0;i<n;i++) scanf("%d",&a[i]); ans=0; merge_sort(a,0,n-1); printf("%I64d\n",ans); } return 0; }