luogu P5367 【模板】康托展开 / P2518 [HAOI2010]计数
今天学习康托展开 和 可重集康托展开。。。
康托展开。。就是给你一个1~n全排列,问它的排名。。
例如:x1, x2 , x3 , x4 , x5 是一个1~5的全排列,现在我们要求它的排名。。
从第1位开始枚举。。
然后枚举j,当然这个j要< xi ,并且不能在x(1~i-1)中出现过。
发现当 j < xi 时 ,后面无论怎样填,都会比所求排列的字典序小。。所以就是! (len-i) 。累加到答案中。
枚举完j后,就令第i位为xi,将xi标记上即可,后面枚举 j 时就不能在枚举到 xi了。
考虑优化枚举 j 这个过程。。
开个树状数组。记录一下前面1~i-1出现过那些数,合法的j的个数即为xi-1-ask(xi - 1)啦,直接乘上 ! (len-i) 即可。。
最后add(xi)。。
然后就是蛤OI的这道计数题啦。。
只不过是可以有相同的元素罢了。。
把上面枚举j统计答案乘!(len-i)的过程改为乘上后面剩余元素可重集的全排列即可。。
其他的大致相同。。这个玩意就不能用树状数组了,只能老老实实枚举 j 。。
由于这个题它TM不取模,,搞得求可重集的全排列的过程很难搞。。
虽然最后结果不会爆long long 但中间也可能会爆。。
所以我们就不能直接按求可重集的全排列的公式来求了。。
用组合数。。而由于我是个蒟蒻,不会用符号,就没有办法演示了。。看代码吧。。
计算组合数要用递推预处理,不然还不如直接搞阶乘上下相消呢(比较麻烦)
P2518计数Code 怎么这么短。。
#include<iostream> #include<cstdio> #include<cstring> #define ll long long using namespace std; char a[100]; int b[11],len; ll ans,c[55][55]; ll clac(int x){ ll res=1; for(int i=0;i<=9;i++){ res*=c[x][b[i]];x-=b[i]; } return res; } int main(){ scanf("%s",a+1);len=strlen(a+1); c[0][0]=1; for(int i=1;i<=50;i++){ c[i][0]=1; for(int j=1;j<=i;j++) c[i][j]=c[i-1][j]+c[i-1][j-1]; } for(int i=1;i<=len;i++) b[a[i]-'0']++; for(int i=1;i<=len;i++){ for(int j=0;j<a[i]-'0';j++){ if(b[j]){ b[j]--; ans+=clac(len-i); b[j]++; } } b[a[i]-'0']--; } printf("%lld\n",ans); }