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>
 2 #include<iostream>
 3 using namespace std;
 4
 5 typedef long long ll;
 6 const int N = 5e6+10;
 7
 8 int mod,n;
 9 ll f[N],fac[N],s[N];
10
11 ll pow(ll a,ll p,int mod)
12 {
13     ll ans=1;
14     while(p) {
15         if(p&1) ans=(ans*a)%mod;
16         a=(a*a)%mod; p>>=1;
17     }
18     return ans;
19 }
20
21 void get_pre(int n)
22 {
23     fac[0]=1;
24     for(int i=1;i<=n;i++)
25         fac[i]=(fac[i-1]*i)%mod;
26 }
27 ll C(ll n,ll m,int mod)
28 {
29     if(n<m) return 0;
30     if(n<mod&&m<mod) {
31         ll invn=pow(fac[n-m],mod-2,mod);
32         ll invm=pow(fac[m],mod-2,mod);
33         return fac[n]*invm%mod*invn%mod;
34     }
35     return C(n/mod,m/mod,mod)*C(n%mod,m%mod,mod)%mod;
36 }
37
38 int main()
39 {
40     scanf("%d%d",&n,&mod);
41     get_pre(min(n,mod));
42     for(int i=n;i;i--) {
43         s[i]=s[i<<1]+s[i<<1|1]+1;
44         f[i]=C(s[i]-1,s[i<<1],mod);
45         if((i<<1)<=n) f[i]=(f[i]*f[i<<1])%mod;
46         if((i<<1|1)<=n) f[i]=(f[i]*f[i<<1|1])%mod;
47     }
48     printf("%lld\n",f[1]);
49     return 0;
50 }
时间: 2024-08-04 14:48:46

bzoj 2111 [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>

【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定理 题目显然小根堆,考虑怎么求以一个节点为根的方案数.根肯定

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

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

组合数学+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 排列计数 组合数

[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 1833: [ZJOI2010]count 数字计数( dp )

dp(i, j, k)表示共i位, 最高位是j, 数字k出现次数. 预处理出来. 差分答案, 对于0~x的答案, 从低位到高位进行讨论 ------------------------------------------------------------------------------ #include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn = 16; const int N =