【题目】
题目描述:
n n n( 1 1 1 ≤ n n n ≤ 20 20 20) 头牛,编号为 1... n 1...n 1...n,正在与 FJ 玩一个疯狂的游戏。奶牛会排成一行(牛线),问 FJ 此时的行号是多少。之后,FJ 会给牛一个行号,牛必须按照新行号排列成线。
行号是通过以字典序对行的所有排列进行编号来分配的。比如说:FJ 有 5 5 5 头牛,让他们排为行号 3 3 3,排列顺序为:
1:1 2 3 4 5
2:1 2 3 5 4
3:1 2 4 3 5
因此,牛将在牛线 1 2 4 3 5 中。
之后,奶牛排列为"1 2 5 3 4",并向 FJ 问他们的行号。继续列表:
4:1 2 4 5 3
5:1 2 5 3 4
FJ 可以看到这里的答案是 5 5 5。
FJ 和奶牛希望你的帮助玩他们的游戏。他们需要 k k k( 1 1 1 ≤ k k k ≤ 10000 10000 10000)组查询,查询有两个部分: c i c_i ci 将是"P"或"Q"的命令。
如果 c i c_i ci 是"P",则查询的第二部分将是一个整数 a i a_i ai( 1 1 1 ≤ a i a_i ai ≤ n ! n! n!),它是行号。此时,你需要回答正确的牛线。
如果 c i c_i ci 是"Q",则查询的第二部分将是 n n n 个不同的整数 b i , j b_{i,j} bi,j( 1 1 1 ≤ b i , j b_{i,j} bi,j ≤ n n n)。这将表示一条牛线,此时你需要输出正确的行号。
样例数据:
输入
5 2
P
3
Q
1 2 5 3 4
输出
1 2 4 3 5
5
【分析】
题解:康拓展开模板题
直接套用模板就可以解这道题了
贴一个同学的代码吧康拓展开&逆康拓展开
【代码】
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 25
#define ll long long
using namespace std;
int n,m,a[N];
bool vis[N];
ll fac[N];
ll cantor()
{
ll ans=0;
for(int i=1;i<=n;++i)
{
int num=0;
for(int j=i+1;j<=n;++j)
if(a[j]<a[i]) num++;
ans+=fac[n-i]*num;
}
return ans;
}
void reverse_cantor(ll x)
{
int i,j;x--;
memset(vis,false,sizeof(vis));
for(i=1;i<=n;++i)
{
int num=x/fac[n-i];
for(j=1;j<=n;++j)
if(!vis[j]&&!num--)
break;
a[i]=j;
vis[j]=true;
x%=fac[n-i];
}
}
int main()
{
int i,j;
char c;ll x;
scanf("%d%d",&n,&m);
fac[0]=fac[1]=1;
for(i=2;i<=n;++i)
fac[i]=fac[i-1]*i;
for(i=1;i<=m;++i)
{
cin>>c;
if(c=='P')
{
scanf("%lld",&x);
reverse_cantor(x);
for(j=1;j<=n;++j)
printf("%d ",a[j]);
printf("\n");
}
else
{
for(j=1;j<=n;++j)
scanf("%d",&a[j]);
x=cantor()+1;
printf("%lld\n",x);
}
}
return 0;
}