BZOJ 2111 [ZJOI2010]Perm 排列计数

Posted ws_zzy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 2111 [ZJOI2010]Perm 排列计数相关的知识,希望对你有一定的参考价值。

题解:发现问题的本质,即堆的个数

动态规划一下

f[i]表示前i个元素形成的堆的个数

第i个元素为根,左右子树又是两个堆

注意:逆元存在条件

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
typedef long long Lint;
const int maxn=1000009;

int n,mm;

Lint f[maxn];
int h[maxn];
Lint g[maxn];
Lint Ksm(Lint a,int p){
	Lint ret=1;
	for(;p;p>>=1,a=a*a%mm){
		if(p&1)ret=ret*a%mm;
	}
	return ret;
}
Lint Inv(Lint x){
	return Ksm(x,mm-2);
}
Lint C(int n,int m){
	int tmp=h[n]-h[m]-h[n-m];
	if(tmp)return 0;
	return f[n]*Inv(f[m])%mm*Inv(f[n-m])%mm;
}

int t=0;
int main(){
	scanf("%d%d",&n,&mm);
	f[0]=1;h[0]=0;
	for(int i=1;i<=n;++i){
		int x=i;h[i]=h[i-1];
		while(x%mm==0){
			x/=mm;++h[i];
		}
		f[i]=f[i-1]*x%mm;
	}
	
	g[0]=g[1]=1;
	for(int i=2;i<=n;++i){
		while((1<<(t+1))-1<=i)++t;
		int res=i-((1<<t)-1);
		if(res>=(1<<(t-1))){
			g[i]=C(i-1,(1<<t)-1)*g[(1<<t)-1]%mm*g[i-(1<<t)]%mm;
		}else{
			g[i]=C(i-1,(1<<(t-1))+res-1)*g[(1<<(t-1))+res-1]%mm*g[(1<<(t-1))-1]%mm;
		}
	}
	cout<<g[n]<<endl;
}

  

 

以上是关于BZOJ 2111 [ZJOI2010]Perm 排列计数的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ2111[ZJOI2010]Perm 排列计数 组合数

bzoj 2111: [ZJOI2010]Perm 排列计数 (dp+卢卡斯定理)

BZOJ 2111 [ZJOI2010]Perm 排列计数

组合数学+lucas定理+逆元 BZOJ2111 [ZJOI2010]Perm 排列计数

bzoj 2111: [ZJOI2010]Perm 排列计数 Lucas

bzoj 2111 [ZJOI2010]Perm 排列计数(DP+lucas定理)