题意:输入一串数字,对其进行一次操作:把潜m个数放到末尾,问最少的pair(i,j)(i<j,a[i]>a[j]),输入的数字各不相同,且在[0,n-1]内
做法:先用线段树的手法求出原始序列的pair数,然后在移动时,可以发现一个规律,设每当进行某次操作,位于序列中的第m个数为X,此时序列中的pair数为p,有一个操作移动了m-1个数,最终的pair数为p,在形成的序列中x为与序列的顶部,要计算的那个移动m个数的操作可以认为是把X移到该序列的末尾,此时,比x大的数有n-1-x个,比X小的数有x个,那么把它移到末尾,会增加n-1-x-x个新pair。这样递推,就可以算出各次操作的pair,得出答案
#include<cstdio> #include<cstring> #define LMT 5003 #define left l,m,x<<1 #define right m+1,r,x<<1|1 int a[LMT],sum[LMT<<2]; int min(int a,int b) { return a<b?a:b; } void update(int key,int l,int r,int x) { if(l==r) { sum[x]=1; return; } int m=(l+r)>>1; if(key<=m)update(key,left); if(key>m)update(key,right); sum[x]=sum[x<<1]+sum[x<<1|1]; } int equery(int L,int R,int l,int r,int x) { if(L<=l&&r<=R)return sum[x]; int m=(l+r)>>1,ret=0; if(L<=m)ret+=equery(L,R,left); if(R>m)ret+=equery(L,R,right); return ret; } int main(void) { int n,ret,ans; while(~scanf("%d",&n)) { ans=ret=0; memset(sum,0,sizeof(sum)); for(int i=0;i<n;i++) { scanf("%d",&a[i]); ret+=equery(a[i]+1,n-1,0,n-1,1);//方便的求出在它之前的比它大的数 update(a[i],0,n-1,1); } ans=ret; for(int i=0;i<n;i++) { ret+=n-1-a[i]-a[i];//进行每一次的转至是,总对数的变换方式 ans=min(ret,ans); } printf("%d\n",ans); } return 0; }