[ZJOI2010]排列计数,数位Dp

本文详细解析了ZJOI2010中排列计数问题的解决思路,通过将问题转化为完全二叉树的子树大小统计,使用递归深度优先搜索算法进行子树大小的计算,并结合组合数学原理,最终得出问题的解答。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

正题

      [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));
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值