hdu 1394
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394
题意:找最少逆序对的和。输入n个数,每个数不大于n-1。且每个数只出现一次。
针对题目的特性,我们可以由第一种情况找出接下来的n-1种情况。
当a0往后移的时候,sum1为sum0加上比a0大的数即(n-1)-a0,减去比a0小的数即a0.
sum1=sum0+(n-1)-a0-a0
sum2=sum1+(n-1)-a1-a1
......
对于求sum0,每次输入一个数后,把ai-(n-1)的区间求一次和,接着在ai处加上一个1。
单纯利用一个暴力的话,时间复杂度变成n^2。于是此处可以利用线段树维护区间和。
详见代码。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 #define lson x,mid,now<<1 6 #define rson mid+1,y,now<<1|1 7 const int maxn=5005; 8 int sum[maxn<<2]; 9 int a[maxn]; 10 11 int query(int l,int r,int x,int y,int now) 12 { 13 if(x>=l&&y<=r){ 14 return sum[now]; 15 } 16 int mid=(x+y)>>1; 17 int s=0; 18 if(mid>=l) s+=query( l, r, lson); 19 if(mid<r) s+=query( l, r, rson); 20 return s; 21 } 22 23 void update(int temp, int x,int y,int now) 24 { 25 if(x==y){ 26 sum[now]++; 27 return ; 28 } 29 int mid=(x+y)>>1; 30 if(temp<=mid) update( temp, lson); 31 else update( temp, rson); 32 sum[now]=sum[now<<1]+sum[now<<1|1]; 33 } 34 35 int main() 36 { 37 int n; 38 while( ~scanf("%d",&n)){ 39 memset( sum, 0, sizeof sum); 40 int sum=0; 41 for(int i=0;i<n;i++){ 42 scanf("%d",&a[i]); 43 sum+=query(a[i],n-1,0,n-1,1); 44 update( a[i], 0, n-1, 1); 45 } 46 int ans=sum; 47 for(int i=0;i<n;i++){ 48 sum+=n-1-a[i]-a[i]; 49 ans=min(ans,sum); 50 } 51 printf("%d\n",ans); 52 } 53 return 0; 54 }