1. 程式人生 > >[ZJOI2010]排列計數,數位Dp

[ZJOI2010]排列計數,數位Dp

正題

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