NKOJ 2272【康托展开】数字排列
时间限制 : 20000 MS 空间限制 : 65536 KB
问题描述
{1,2,3} 三个数的全排列可看做6个数字,按从小到大排序得到序列a:123,132,213,231,312,321
下面有两种提问:
1.数字231在序列a中排名第几?回答4
2.数列a中排名第5的数字是多少?回答312
给出n个数字(1,2,3,…,n),回答关于序列a的1和2两种提问。
输入格式
第一行,两个整数n和m,n表示数字1到n(n<=9)构成的全排列,m(m<=100,000)表示询问数
接下来m行,表示询问,每行两个整数x和y,x=1表示第一种询问,回答y的排名;x=2表示第2种询问,答出排名为y的数字
输出格式
m行,每行一个整数,表示对应的答案。
样例输入
3 2
1 231
2 5
样例输出
4
312
注意:区分1~n、0~n、0不能再最前的情况
#include<cstdio>
#include<iostream>
using namespace std;
const int need=11;
int p[need],s[need];
int n,m;
int kt()
{
int ans=0,cnt;
for(int i=1;i<=n;i++)
{
cnt=0;
for(int j=1;j<i;j++) if(s[j]<s[i]) cnt++;
ans+=(s[i]-1-cnt)*p[n-i];//1~n 的排列,多减1,0~n,不减1
}
return ans+1;
}
void nkt(int k)//k为排名
{
bool mark[need]={0};
k--;
for(int i=1,j,t;i<=n;i++)
{
t=k/p[n-i];
for(j=1;j<=n;j++)
if(!mark[j])
{
if(t==0) break;
t--;
}
mark[j]=true;
s[i]=j;
k%=p[n-i];
}
}
int main()
{
scanf("%d%d",&n,&m);
p[0]=1;
for(int i=1;i<=n;i++) p[i]=p[i-1]*i;
char a;
for(int i=1,a,b,tot;i<=m;i++)
{
tot=1;
scanf("%d",&a);
if(a==1)
{
while(true)
{
a=getchar();
if('0'<a&&a<='9') break;
}
s[1]=a-'0';
while(true)
{
a=getchar();
if(a<='0'||a>'9') break;
s[++tot]=a-'0';
}
printf("%d\n",kt());
}
else
{
scanf("%d",&b);
nkt(b);
for(int i=1;i<=n;i++) putchar(s[i]+'0');putchar(10);
}
}
}