zoukankan      html  css  js  c++  java
  • ●洛谷P2606 [ZJOI2010]排列计数

    题链:

    https://www.luogu.org/problemnew/show/P2606
    题解:

    组合数(DP),Lucas定理

    首先应该容易看出,这个排列其实是一个小顶堆。
    然后我们可以考虑dp:
    令F[i]为小顶堆的i号节点那棵子树的方案数:
    F[i]=F[i*2]*F[i*2+1]*C(size[i]-1,size[i*2])
    含义就是左儿子的方案数*右儿子的方案数*当前i节点取走最小的那个值后分size[i*2]个数给左儿子的方案数。

    (BZOJ上数据加强,可能会N>P,所以如果直接预处理阶乘和阶乘逆元可能会导致出现很多不该出现的0,所以这里考虑用Lucas定理)


    代码:

    #include<bits/stdc++.h>
    #define MAXN 1000006
    using namespace std;
    int N,P,ANS=1;
    int size[MAXN],fac[MAXN],inv[MAXN];
    int fastpow(int a,int b){
    	int ret=1; 
    	if(a==0) return 1;
    	for(;b;a=1ll*a*a%P,b>>=1)
    		if(b&1) ret=1ll*ret*a%P;
    	return ret;
    }
    void prepare(int m){
    	fac[0]=inv[0]=1;
    	for(int i=1;i<=m;i++) fac[i]=1ll*fac[i-1]*i%P;
    	inv[m]=fastpow(fac[m],P-2);
    	for(int i=m-1;i>=1;i--) inv[i]=1ll*inv[i+1]*(i+1)%P;
    }
    int C(int m,int n){
    	int ret=1,nn,mm;
    	while(m&&n){
    		mm=m%P; nn=n%P; m/=P; m/=P;
    		if(mm<nn) return 0;
    		ret=1ll*ret*fac[mm]%P*inv[nn]%P*inv[mm-nn]%P;
    	}
    	return ret;
    }
    int main(){
    	scanf("%d%d",&N,&P);
    	prepare(min(N,P-1));
    	for(int i=N;i>=1;i--) size[i]++,size[i/2]+=size[i];
    	for(int i=1;i<=N;i++) if(i*2<=N) 
    		ANS=1ll*ANS*C(size[i]-1,size[i*2])%P;
    	printf("%d
    ",ANS);
    	return 0;
    }
    

      

  • 相关阅读:
    React之React.cloneElement
    HTB-靶机-Vault
    HTB-靶机-Curling
    HTB-靶机-Zipper
    HTB-靶机-Frolic
    HTB-靶机-Carrier
    HTB-靶机-Oz
    HTB-靶机-Dab
    HTB-靶机-Waldo
    HTB-靶机-Reddish
  • 原文地址:https://www.cnblogs.com/zj75211/p/8541642.html
Copyright © 2011-2022 走看看