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; }