卧薪尝胆,厚积薄发。
USACO11FEB SILVER 牛线Cow Line
Date: Sat Oct 13 20:08:07 CST 2018
In Category:
NoCategory
Description:
康托展开模板。
$1\leqslant n\leqslant 20$
Solution:
对于一个排列,康托展开的公式是:
$$
x=\sum_{i=1}^n(n-i)!\sigma_i
$$
$\sigma_i$
是比
$a[i]$
小且在
$a[i]$
之前没有出现过的数的个数。
逆康托展开大概就是先把
$k-1$
然后第
$i$
位除
$(n-i)!$
,找还没有用过的第这个数的数。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int n,m;
char getc()
{
register char c = getchar();
while(c != 'P' && c != 'Q')c = getchar();
return c;
}
long long fac[21];
int a[21];
void build()
{
for(int i = 1;i <= n;++i)scanf("%d",&a[i]);
long long res = 0;
for(int i = 1;i <= n;++i)
{
int sum = a[i] - 1;
for(int j = 1;j < i;++j)
{
if(a[j] < a[i])--sum;
}
res += sum * fac[n - i];
}
cout << res + 1 << endl;
return;
}
int v[21];
void rever()
{
long long k;
scanf("%lld",&k);--k;
for(int i = 1;i <= n;++i)v[i] = i;
int p = n;
for(int i = 1;i <= n;++i)
{
int s = k / fac[n - i];k = k % fac[n - i];
cout << v[s + 1] << " ";
for(int j = s + 1;j < p;++j)v[j] = v[j + 1];--p;
}
cout << endl;
return;
}
int main()
{
fac[0] = 1;
for(int i = 1;i <= 20;++i)fac[i] = fac[i - 1] * i;
scanf("%d%d",&n,&m);
char c;
for(int i = 1;i <= m;++i)
{
c = getc();
if(c == 'P')rever();
else build();
}
return 0;
}
In tag:
数学-康托展开
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡