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>
#include <iostream>
#include <algorithm>
#define M 1001001
using namespace std;
typedef long long ll;
ll n,p,fac[M],inv[M],size[M<<1],f[M];
void Linear_Shaker()
{
    int i;
    fac[0]=1;
    for(i=1;i<=n&&i<p;i++)
        fac[i]=fac[i-1]*i%p;
    inv[1]=1;
    for(i=2;i<=n&&i<p;i++)
        inv[i]=(p-p/i)*inv[p%i]%p;
    inv[0]=1;
    for(i=1;i<=n&&i<p;i++)
        inv[i]=inv[i]*inv[i-1]%p;
}
ll C(ll x,ll y)
{
    if(x<y) return 0;
    if(x<p&&y<p)
        return fac[x]*inv[y]%p*inv[x-y]%p;
    return C(x/p,y/p)*C(x%p,y%p)%p;
}
int main()
{
    int i;
    cin>>n>>p;
    Linear_Shaker();
    for(i=n;i;i--)
    {
        size[i]=size[i<<1]+size[i<<1|1]+1;
        f[i]=C(size[i]-1,size[i<<1])*( (i<<1)>n?1:f[i<<1] )%p*( (i<<1|1)>n?1:f[i<<1|1] )%p;
    }
    cout<<f[1]<<endl;
}
时间: 2024-10-26 03:36:06

BZOJ 2111 ZJOI2010 Perm 排列计数 组合数学+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 排列计数(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>

【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 排列计数 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 样例

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(