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(s[i]-1,s[i<<1])

然后就lucas定理算算组合数就可以了。

代码:

 1 #include<cstdio>
 2
 3 #include<cstdlib>
 4
 5 #include<cmath>
 6
 7 #include<cstring>
 8
 9 #include<algorithm>
10
11 #include<iostream>
12
13 #include<vector>
14
15 #include<map>
16
17 #include<set>
18
19 #include<queue>
20
21 #include<string>
22
23 #define inf 1000000000
24
25 #define maxn 2000000+5
26
27 #define maxm 200000+5
28
29 #define eps 1e-10
30
31 #define ll long long
32
33 #define pa pair<int,int>
34
35 #define for0(i,n) for(int i=0;i<=(n);i++)
36
37 #define for1(i,n) for(int i=1;i<=(n);i++)
38
39 #define for2(i,x,y) for(int i=(x);i<=(y);i++)
40
41 #define for3(i,x,y) for(int i=(x);i>=(y);i--)
42
43 #define for4(i,x) for(int i=head[x],y=e[i].go;i;i=e[i].next,y=e[i].go)
44
45 #define for5(n,m) for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)
46
47 #define mod 1000000007
48
49 using namespace std;
50
51 inline int read()
52
53 {
54
55     int x=0,f=1;char ch=getchar();
56
57     while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
58
59     while(ch>=‘0‘&&ch<=‘9‘){x=10*x+ch-‘0‘;ch=getchar();}
60
61     return x*f;
62
63 }
64 int s[maxn];
65 ll  n,m,p,f[maxn],fac[maxn],inv[maxn];
66 inline ll c(int n,int m)
67 {
68     if(n<m)return 0;
69     if(n<p&&m<p)return fac[n]*inv[m]%p*inv[n-m]%p;
70     return c(n/p,m/p)*c(n%p,m%p)%p;
71 }
72
73 int main()
74
75 {
76
77     freopen("input.txt","r",stdin);
78
79     freopen("output.txt","w",stdout);
80
81     n=read();p=read();m=min(n,p-1);
82     fac[0]=1;
83     for1(i,m)fac[i]=fac[i-1]*(ll)i%p;
84     inv[0]=inv[1]=1;
85     for2(i,2,m)inv[i]=(ll)(p/i+1)*inv[i-p%i]%p;
86     for2(i,2,m)inv[i]=inv[i]*inv[i-1]%p;
87     for3(i,n,1)
88     {
89         s[i]=s[i<<1]+s[i<<1|1]+1;
90         f[i]=((i<<1)>n?1:f[i<<1])*((i<<1|1)>n?1:f[i<<1|1])%p*c(s[i]-1,s[i<<1])%p;
91     }
92     cout<<f[1]<<endl;
93
94     return 0;
95
96 }  

时间: 2024-10-07 16:12:36

BZOJ2111: [ZJOI2010]Perm 排列计数的相关文章

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

题目大意:求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>

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

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*