bzoj3301: [USACO2011 Feb] Cow Line
新姿势康托展开。。
------------------------------------
裸的康托展开&逆康托展开
康托展开就是一种特殊的hash,且是可逆的……
康托展开计算的是有多少种排列的字典序比这个小,所以编号应该+1;逆运算同理(-1)。
序列->序号:(康托展开)
对于每个数a[i],数比它小的数有多少个在它之前没出现,记为b[i],ans=1+∑b[i]∗(n−i)!ans=1+∑b[i]∗(n−i)!
序号->序列:(逆康托展开)
求第x个排列所对应的序列,先将x-1,然后对于a[i],⌊x(n−i)!⌋⌊x(n−i)!⌋即为在它之后出现的比它小的数的个数,所以从小到大数一下有几个没出现的数,就知道a[i]是第几个数了。
------------------------------------
1 #include<bits/stdc++.h> 2 #define rep(i,l,r) for(int i=l;i<=r;++i) 3 using namespace std; 4 const int N=25; 5 int n,Q,a[N],f[N]; 6 typedef long long ll; 7 char s[10]; 8 ll ans,sum,fac[N],x; 9 int main(){ 10 scanf("%d%d",&n,&Q); 11 fac[1]=1; fac[0]=1; 12 rep(i,2,n) fac[i]=fac[i-1]*i; 13 while(Q--){ 14 scanf("%s",s); 15 if(s[0]=='P'){ 16 scanf("%lld",&x);--x; 17 memset(f,0,sizeof f); 18 rep(i,1,n){ 19 int t=x/fac[n-i],j,k; 20 for(k=1,j=0;j<=t;k++) if (!f[k]) j++; 21 f[k-1]=1; a[i]=k-1; 22 x%=fac[n-i]; 23 } 24 rep(i,1,n-1) printf("%d ",a[i]); 25 printf("%d\n",a[n]); 26 }else { 27 memset(f,0,sizeof f); ans=1; 28 rep(i,1,n) { 29 scanf("%lld",&x); 30 f[x]=1; 31 sum=0; 32 rep(j,1,x-1) sum+=(!f[j]); 33 ans+=sum*fac[n-i]; 34 } 35 printf("%lld\n",ans); 36 } 37 } 38 }