版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/84780544
题目链接:哆啦A梦传送门
题意:给你一串n个值,每个a[i] 范围在 [ 0 , n-1 ] ,然后我们可以把序列的头部移到尾部,这样就有n中不同的序列,问:在这n条序列中逆序数最少的是多少?
题解:我们可以用树状数组,具体看代码注释。
树状数组模板:树状数组
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=5050;
int tree[maxn],n;
int a[maxn];
void add(int k,int num)
{
while(k<=n){
tree[k]+=num;
k+=k&-k;
}
}
int read(int k) ///计算前k项和
{
int sum=0;
while(k)
{
sum+=tree[k];
k-=k&-k;
}
return sum;
}
int main()
{
while(~scanf("%d",&n))
{
memset(tree,0,sizeof(tree));
int ans=0; ///ans代表初始串的逆序数有多少对
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
a[i]++; ///加一,统一下从1到n
///因为我们是按顺序把a[i]插入到相应的位置,故read(n)-read(a[i])表示
///在a[i]未插入之前比a[i]大的数有多少个,很显然这些数一定能跟后插入的a[i]组成逆序对
ans=ans+read(n)-read(a[i]);
add(a[i],1);///在位置a[i]上加一
}
int result=ans; ///储存逆序数的最小值
///我们按顺序把序列的头部移到尾部,然后每次处理下移到尾部之后逆序数的改变
for(int i=1;i<=n;i++)
{
///把a[i]移到尾部,因为a[i]在头部,故有a[i]-1个数(比a[i]小)能跟a[i]组成逆序对,减掉
///当a[i]移到尾部之后,因为a[i]在尾部,故有n-a[i]个数(比a[i]大)能与a[i]组成逆序对,加上
ans=ans-(a[i]-1)+(n-a[i]);
if(ans<result) result=ans;
}
printf("%d\n",result);
}
return 0;
}
这题应用树状数组解决得很妙啊,我们来总结一下,按常理看,我们求逆序数,是在这个数之后的数中去数逆序对,但这里用的就很妙,是在这个数之前的数中去数逆序对,这里逆向思维就很妙了,因为这样就可以很完美的用树状数组求前区间和去处理了,故我们以后想要用树状数组去解决问题,应该尽量联系到前区间。