http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=38458
给你一个n,m,表示原序列的长度和逆序对数,构造出字典序最小的原序列。
直接1到n排列;
当序列完全倒序,长度为i,其逆序对为 i乘(i-1)/2,
则我们把所有的 逆序对数列出来, 1 3 6 10 15.....
对于需要的逆序对m,我们找到大于m的第一个数,例如m=14,则我们找逆序对为15对应的i=5;
然后把原序列的最后5个倒序(字典最小)
得到 1 2 3 4 .......n n-1 n-2 n-3 n-4 n-5
然后此时我们看res= i乘(i-1)/2-m,得到多出来的逆序对,则例如res=3,我们只需要把后面逆序这段的第res(三)个数挪到最前面即可。
即 1 2 3 4 .......n n-1 n-2 n-3 n-4 n-5变成
1 2 3 4 .......n-2 n n-1 n-3 n-4 n-5 既符合个数,又是字典序最小
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <map>
#include <set>
#include <vector>
#include <iostream>
using namespace std;
int ans[50000+5];
int num[50000+5];
void rever(int x,int y)
{
if (x>y) return;
int end=x+(y-x)/2;
for (int i=x;i<=end;i++)
{
swap(ans[i],ans[y-i+x]);
}
}
int main()
{
int n;
int m;
while (scanf("%d%d",&n,&m)!=EOF && n!=-1&&m!=-1)
{
for (int i=1;i<=n ;i++)
ans[i]=i;
int flag=0;
for (int i=1;i<=n;i++)
if (i*(i-1)/2>=m){flag=i ;break;}
rever(n-flag+1,n);
int res=(flag*(flag-1)/2)-m;
flag=n-flag+1;
for (int i=1;i<=flag-1;i++)
num[i]=ans[i];
num[flag]=ans[flag+res];
int j=flag;
for (int i=flag+1;i<=n;i++)
{
if (j==flag+res) j++;
num[i]=ans[j];
j++;
}
for (int i=1;i<=n;i++)
{
if (i>1) printf(" ");
printf("%d",num[i]);
}
printf("\n");
}
return 0;
}