BZOJ2111 [ZJOI2010]Perm 排列计数

来源:互联网 发布:网络教育高起专专升本 编辑:程序博客网 时间:2024/06/05 01:00

我们把整个序列看成一棵树,x的左儿子是2x,右儿子是2x+1

这样问题就变成了给这棵树标号,儿子的标号必须比父亲大

dp即可

f[i]表示以i为根的子树,在标号集合已经确定的情况下的标号方案数

如果i是叶子,f[i]=1

如果i只有1个儿子,f[i]=f[2i]

如果i有两个儿子,f[i]=f[2i]*f[2i+1]*C(siz[2i]+siz[2i+1],siz[2i])

siz为子树大小

算组合数用lucas

#include<iostream>#include<cstdlib>#include<cstdio>#include<cstring>#include<ctime>#include<cmath>#include<algorithm>#include<iomanip>#include<queue>#include<map>#include<bitset>#include<stack>#include<vector>#include<set>using namespace std;#define MAXN 1000010#define MAXM 1010#define INF 1000000000#define MOD 1000000007#define ll long long#define eps 1e-8int n,p;ll fac[MAXN],ine[MAXN];ll f[MAXN];int siz[MAXN];ll C(int n,int m){if(m>n){return 0;}if(n<p){return fac[n]*ine[m]%p*ine[n-m]%p;}return C(n/p,m/p)*C(n%p,m%p)%p;}int main(){int i;scanf("%d%d",&n,&p);fac[0]=ine[0]=ine[1]=1;for(i=1;i<=n&&i<p;i++){fac[i]=fac[i-1]*i%p;}for(i=2;i<=n&&i<p;i++){ine[i]=(p-p/i)*ine[p%i]%p;}for(i=2;i<=n&&i<p;i++){(ine[i]*=ine[i-1])%=p;}for(i=n;i;i--){if(i*2+1<=n){siz[i]=1+siz[i*2]+siz[i*2+1];f[i]=f[i*2]*f[i*2+1]%p*C(siz[i]-1,siz[i*2])%p;}else if(i*2<=n){f[i]=f[i*2];siz[i]=1+siz[i*2];}else{f[i]=1;siz[i]=1;}}printf("%lld\n",f[1]);return 0;}/**/


0 0
原创粉丝点击