【bzoj2111】[ZJOI2010]Perm 排列计数 dp+Lucas定理

题目描述

称一个1,2,...,N的排列P1,P2...,Pn是Mogic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Mogic的,答案可能很大,只能输出模P以后的值

输入

输入文件的第一行包含两个整数 n和p,含义如上所述。

输出

输出文件中仅包含一个整数,表示计算1,2,?, n的排列中, Mogic排列的个数模 p的值。

样例输入

20 23

样例输出

16



题解

dp+Lucas定理

题目显然小根堆,考虑怎么求以一个节点为根的方案数。根肯定是最小的节点,剩余$n-1$个数选择左子树大小个作为左子树,其余作为右子树。

设$f[i]$表示以i为根的子树形成小根堆的方案数,那么$f[i]=C_{si[i]-1}^{si[i<<1]}*f[i<<1]*f[i<<1|1]$。

注意处理某子树为空的方案数。

另外本题没有保证$n\le p$,故组合数需要使用Lucas定理求出。

#include <cstdio>
#define N 1000010
typedef long long ll;
ll fac[N] , inv[N] , fin[N] , f[N << 1] , si[N << 1];
int p;
ll choose(int n , int m)
{
	if(n < m) return 0;
	if(n < p && m < p) return fac[n] * fin[m] % p * fin[n - m] % p;
	else return choose(n / p , m / p) * choose(n % p , m % p) % p;
}
int main()
{
	int n , i;
	scanf("%d%d" , &n , &p);
	fac[0] = fac[1] = inv[1] = fin[0] = fin[1] = f[0] = 1;
	for(i = 2 ; i <= n ; i ++ )
	{
		fac[i] = fac[i - 1] * i % p;
		inv[i] = (p - p / i) * inv[p % i] % p;
		fin[i] = fin[i - 1] * inv[i] % p;
	}
	for(i = n ; i ; i -- )
	{
		si[i] = si[i << 1] + si[i << 1 | 1] + 1;
		f[i] = choose(si[i] - 1 , si[i << 1]) * ((i << 1) > n ? 1 : f[i << 1]) % p * ((i << 1 | 1) > n ? 1 : f[i << 1 | 1]) % p;
	}
	printf("%lld\n" , f[1]);
	return 0;
}
时间: 2024-12-17 05:56:04

【bzoj2111】[ZJOI2010]Perm 排列计数 dp+Lucas定理的相关文章

BZOJ 2111 ZJOI2010 Perm 排列计数 组合数学+Lucas定理

题目大意:求1~n的排列能组成多少种小根堆 考虑一个1~i的排列所构成的堆,l为左儿子大小,r为右儿子的大小 那么1一定是堆顶 左儿子和右儿子分别是一个堆 显然如果选出l个数给左儿子 那么左儿子的方案数显然是f[l],右儿子的方案数为f[r] 于是有f[i]=C(i-1,l)*f[l]*f[r] 于是我们线性筛处理出阶乘和阶乘的逆元 代入即可得到WA 原因是这题n可以大于p 此时要用到Lucas定理 坑死了 #include <cstdio> #include <cstring>

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

2111: [ZJOI2010]Perm 排列计数 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 2118  Solved: 563[Submit][Status][Discuss] Description 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值 Input 输入文件的第一行包含两个

BZOJ2111: [ZJOI2010]Perm 排列计数

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2111 题意:一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值 题解:注意到形成一个树状结构,如果不妨设f[i]为i所在子树分配s[i]个节点的方案数. 那么有递推式:f[i]=f[i<<1]*f[i<<1|1]*c(

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

bzoj 2111: [ZJOI2010]Perm 排列计数 1 ≤ N ≤ 10^6, P≤ 10^9 题意:求1~N的排列有多少种小根堆 1: #include<cstdio> 2: using namespace std; 3: const int N = 1e6+5; 4: typedef long long LL; 5: LL m, p, T, x, y, F[N]; 6: LL n, size[N<<1]; 7: LL f[N]; 8: LL inv(LL t, LL

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

[BZOJ2111][ZJOI2010]Perm 排列计数 Description 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值 Input 输入文件的第一行包含两个整数 n和p,含义如上所述. Output 输出文件中仅包含一个整数,表示计算1,2,?,的排列中, Magic排列的个数模 p的值. Sample Input 20 23

ZJOI2010 Perm 排列计数

[ZJOI2010]Perm 排列计数 时间限制: 1 Sec  内存限制: 259 MB 题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值 输入 输入文件的第一行包含两个整数 n和p,含义如上所述. 输出 输出文件中仅包含一个整数,表示计算1,2,?, ???的排列中, Magic排列的个数模 p的值. 样例输入 20 23 样例

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

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2111 [题意] 给定n,问1..n的排列中有多少个可以构成小根堆. [思路] 设f[i]为以i为根的方案数,设l为左子树大小r为右子树大小,则有: f[i]=C(i-1,l)*f[l]*f[r] 因为是个堆,所以子树大小都是确定的,可以直接递推得到. 其中C(n,m) nm比较大,可以用lucas定理求. 模型建立的重要性可知一二... [代码] 1 #include<cstdio>

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

code: #include <bits/stdc++.h> #define N 2000004 #define LL long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; LL mod; LL fac[N],inv[N],f[N],size[N]; LL qpow(LL x,LL y) { LL tmp=1ll; for(;y;y>>=1,x=x*

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