[luoguP2606] [ZJOI2010]排列计数(DP)
如果能够根据题意看出这是一个堆的话,那么就有些思路了。。
首先堆顶必须是最小元素,然后左右儿子可以预处理出来都有多少个数,
把剩余的数任意分配给两个儿子,用排列组合即可
dp(now) = dp(now << 1) * dp(now << 1 | 1) * C(sum[now] - 1, sum[now << 1])
#include <cstdio>
#define N 5000001
#define LL long long
int n;
LL p, inv[N], A[N], B[N], s[N];
inline LL C(int x, int y)
{
	return A[x] * B[y] % p * B[x - y] % p;
}
inline LL dp(int now)
{
	if(!s[now] || s[now] == 1) return 1;
	return dp(now << 1) * dp(now << 1 | 1) % p * C(s[now] - 1, s[now << 1]) % p;
}
int main()
{
	int i, j;
	scanf("%d %lld", &n, &p);
	inv[1] = A[1] = A[0] = B[0] = B[1] = 1;
	for(i = 2; i <= n; i++)
	{
		inv[i] = -(p / i) * inv[p % i] % p;
		A[i] = A[i - 1] * i % p;
		B[i] = B[i - 1] * inv[i] % p;
	}
	for(i = 1; i <= n; i++)
		for(j = i; j; j >>= 1) s[j]++;
	printf("%lld\n", (dp(1) + p) % p);
	return 0;
}

 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号