裸的康托展开&逆康托展开
康托展开就是一种特殊的hash,且是可逆的……
康托展开计算的是有多少种排列的字典序比这个小,所以编号应该+1;逆运算同理(-1)。
序列->序号:(康托展开)
对于每个数a[i],数比它小的数有多少个在它之前没出现,记为b[i],$ans=1+\sum b[i]* (n-i)!$
序号->序列:(逆康托展开)
求第x个排列所对应的序列,先将x-1,然后对于a[i],$\left\lfloor \frac{x}{(n-i)!} \right\rfloor $即为在它之后出现的比它小的数的个数,所以从小到大数一下有几个没出现的数,就知道a[i]是第几个数了。
然而这题在比较答案的时候不忽略行末空格……大家小心一点……
1 /************************************************************** 2 Problem: 3301 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:84 ms 7 Memory:1276 kb 8 ****************************************************************/ 9 10 //BZOJ 3301 11 #include<cstdio> 12 #include<cstring> 13 #include<cstdlib> 14 #include<iostream> 15 #include<algorithm> 16 #define rep(i,n) for(int i=0;i<n;++i) 17 #define F(i,j,n) for(int i=j;i<=n;++i) 18 #define D(i,j,n) for(int i=j;i>=n;--i) 19 #define pb push_back 20 using namespace std; 21 typedef long long LL; 22 inline LL getint(){ 23 LL r=1,v=0; char ch=getchar(); 24 for(;!isdigit(ch);ch=getchar()) if (ch=='-') r=-1; 25 for(; isdigit(ch);ch=getchar()) v=v*10-'0'+ch; 26 return r*v; 27 } 28 const int N=25; 29 /*******************template********************/ 30 int n,m; 31 LL fac[N]; 32 int a[N]; 33 bool vis[N]; 34 void pailie(LL x){ 35 memset(vis,0,sizeof vis); 36 F(i,1,n){ 37 int t=x/fac[n-i],j,k; 38 for(k=1,j=0;j<=t;k++) if (!vis[k]) j++; 39 vis[k-1]=1; a[i]=k-1; 40 x%=fac[n-i]; 41 } 42 F(i,1,n-1) printf("%d ",a[i]); 43 printf("%d\n",a[n]); 44 } 45 void hanghao(){ 46 LL ans=1; 47 memset(vis,0,sizeof vis); 48 F(i,1,n){ 49 int j=0,k; 50 vis[a[i]]=1; 51 F(k,1,a[i]) if (!vis[k]) j++; 52 ans+=j*fac[n-i]; 53 } 54 printf("%lld\n",ans); 55 } 56 int main(){ 57 #ifndef ONLINE_JUDGE 58 freopen("3301.in","r",stdin); 59 freopen("3301.out","w",stdout); 60 #endif 61 n=getint(); m=getint(); 62 fac[0]=1; 63 F(i,1,20) fac[i]=fac[i-1]*i; 64 // F(i,0,20) printf("%lld ",fac[i]); puts(""); 65 char cmd[5]; 66 while(m--){ 67 scanf("%s",cmd); 68 if (cmd[0]=='P'){ 69 LL x=getint()-1; 70 pailie(x); 71 }else{ 72 F(i,1,n) a[i]=getint(); 73 hanghao(); 74 } 75 } 76 return 0; 77 } 78