题目地址:http://acm.uestc.edu.cn/problem.php?pid=1622
题目分析:求一个排列的逆序对(如果你不知道逆序对,百度就行了),用相同元素构造一个新的排列,逆序对要相同,字典需要最小。
解题思路:这道题就是求原先的排列的逆序对,求逆序对可以用树状数组做,再构造出新的排列。
构造方法可以这样:
n个数最大的逆序对是(n-1)×n/2,那么可以求出最后K(k:1 to n)个元素全部反序,分别能得到多少逆序对。
从前往后,当后k个元素全部反序的逆序对小于原序列的逆序对时(设差值为step),就把从第k+step-2个元素到第k-1元素从后往前依次往后挪一个,第k-1个元素变成原来第k+step-1个元素。
最后把从第k个元素开始直至最后的元素(此时依然为升序)反序。
源代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#define PN printf("\n");
char chret;
#define SCRN while(scanf("%c",&chret) && chret!='\n');
using namespace std;
int n,org[2012],tar[2012],check[2012],pairs;
int tree[2012];
void up(int t)
{
int i;
while(t<=n)
{
tree[t]++;
t+=t&(-t);
}
}
int down(int t)
{
int sum=0;
while(t>0)
{
sum+=tree[t];
t-=t&(-t);
}
return sum;
}
void create_tar()
{
int i,t,step,j;
for(i=1;i<=n;i++)
{
tar[i]=i;
check[i]=(n-i+1)*(n-i)/2;//n-i+1个元素能构成的最大逆序对
}
i=1;
while(i<=n && pairs>0)
{
if(pairs>check[i+1])
{
step=pairs-check[i+1];
t=tar[i+step];
for(j=i+step;j>i;j--)
tar[j]=tar[j-1];
tar[i]=t;
i++;
step=i;
for(j=0;i<=(step+n)/2;i++,j++)
{
t=tar[i];
tar[i]=tar[n-j];
tar[n-j]=t;
}
return ;
}
i++;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
pairs=0;
memset(tree,0,sizeof(tree));
for(int i=1; i<=n; i++)
{
scanf("%d",&org[i]);
pairs+=down(org[i]);
up(org[i]);
}
pairs=n*(n-1)/2-pairs;
printf("%d\n",pairs);
create_tar();
for(int i=1; i<n; i++)
printf("%d ",tar[i]);
printf("%d",tar[n]);
PN
}
return 0;
}