题目的意思一开始没有看清楚,题目的意思是:
给你一段序列a1,a2,a3,a4,a5,a6,a7,,然后这段序列可以一直调动第一个数字到最后,例如:
a2,a3,a4,a5,a6,a7,a1,…………直到循环了一遍。然后从这么多的序列中求出逆序列最小的一组。注意,序列中的数是从0开始计数的。一开始做还以为给的数字是随意给的呢,笑话,晕了好久。哈哈,切入正题,这道题还是树状数组,但是这道题有了别样的技巧,对于我这种小菜,算是吧。(大牛们做这个可能跟喝白开水一样,所以算不上技巧)。
一开始,挺笨的,直接列举全部序列,然后就一段一段用树状数组求出各自的逆序列,哎,序列给的可是5000啊,列举全部,就是5000个不同的序列,然后每个再求其逆序列,肯定TLE啊。
OK,说说收获吧,原来不用保存全部的序列,是有数学规律的,我一开始想不到,当然跟我没有理解好题意有很大关系。记住,上面说过序列里面的数是从0开始计数的,比如序列可能为0,4,2,1,6,3,5,然后它的逆序列为多少呢?小学生都会数啊,呵呵,当然是sum=0+0+1+2+0+2+1=6啦,那么另一个序列5,0,4,2,1,6,3,是多少?
想想,5换到最前去了(为什么不将5从前往后换呢?因为从后面往前换,好算一点),接下来注意,,那么原来比5大的个数1,此刻就
要用sum-1;因为5掉到最前,那么原来比5大的数,原来是逆序,现在恰好它变为正序了。那么原来比5小的个数5就变为逆序了,那么
(sum-1)+5,,算算看对不对。此刻sum2=sum-1+5=6-1+5=10;
刚刚好序列2的sum2=0+1+1+2+3+0+3=10;哈哈,数学真伟大。
#include<iostream>
#define lowbit(x) x&(-x)
#define M 5001
using namespace std;
int flag[5001];
int add(int n)
{
while(n<=M)
{
flag[n]++;
n+=lowbit(n);
}
return 0;
}
int subsum(int n)
{
int sum=0;
while(n>0)
{
sum+=flag[n];
n-=lowbit(n);
}
return sum;
}
int main(void)
{
int number[5001],n;
while(cin>>n)
{
int count=1,k=0,h=1,i,min=2100000000,sum,lmin,lmax;
for( i=1;i<=n;i++)
scanf("%d",&number[i]);
sum=0;
memset(flag,0,sizeof(flag));
for(i=1;i<=n;i++)
{
//cout<<lmin<<endl;
lmax=i-1-subsum(number[i]+1);
add(number[i]+1);
sum+=lmax;
//cout<<lmax[i]<<" "<<lmin[i]<<endl;
}
// cout<<sum<<endl;
for(i=n;i>1;i--)
{ //注意在这道题中,每个number[i]都加上了1,因为树状数组处理的时候,不能处理0
sum+=number[i]-(n-(number[i]+1));//很神奇,找出规律,就是比number[i]+1大的数,前面比他大的个数为n-(number[i]+1),
//那么比它小的,当然就是number[i]+1-1啦,呵呵,又数数学规律
// cout<<sum<<endl;
if(min>sum)
min=sum;
}
// cout<<sum<<endl;
//cout<<max<<endl;
cout<<min<<endl;
}
return 0;
}
改的很乱,哈哈,见谅!