[ZJOI2010]排列计数,数位Dp

版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢 https://blog.csdn.net/Deep_Kevin/article/details/83003348

正题

      [ZJOI2010]排列计数

      首先,我们先转化问题,要求P_i>P_{i/2},就相当于要求P_i<P_{i*2} and P_i<P_{i*2+1}

      那么这就像当与一棵n个节点的完全二叉树,那么我们统计一下每棵子树的大小,然后下去更新算一下组合数就可以了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;

int n;
long long p;
int l[1000010];
int tot[1000010];
long long fac[1000010],fav[1000010];

void dfs(int x){
	tot[x]=1;
	if(x*2<=n) {
		dfs(x*2);
		tot[x]+=tot[x*2];
		l[x]=tot[x*2];
	}
	if(x*2+1<=n) {
		dfs(x*2+1);
		tot[x]+=tot[x*2+1];
	}
}

long long C(int x,int y){
	return fac[x]*fav[x-y]%p*fav[y]%p;
}

long long F(int x){
	if(x*2+1<=n) return F(x*2)*F(x*2+1)%p*C(tot[x]-1,l[x])%p;
	else if(x*2<=n) return F(x*2);
	else return 1;
}

long long ksm(long long x,long long t){
	long long tot=1;
	while(t>0){
		if(t%2==1){
			tot*=x;
			tot%=p;
		}
		t/=2;
		x*=x;
		x%=p;
	}
	return tot;
}

long long inv(long long x){
	return ksm(x,p-2);
}

int main(){
	scanf("%d %lld",&n,&p);
	fac[0]=1;
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%p;
	fav[n]=inv(fac[n]);
	for(int i=n-1;i>=1;i--) fav[i]=fav[i+1]*(i+1)%p;
	dfs(1);
	printf("%lld\n",F(1));
} 

猜你喜欢

转载自blog.csdn.net/Deep_Kevin/article/details/83003348