Luogu2606[ZJOI2010] 排列计数

原题链接:https://www.luogu.org/problemnew/show/P2606

排列计数

题目描述

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

输入输出格式
输入格式:

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

输出格式:

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

输入输出样例
输入样例#1:

20 23

输出样例#1:

16

说明

100%的数据中,1 ≤N ≤ 10^6, P≤ 10^9,p是一个质数。

题解

条件是 P i &gt; P i / 2 P_i&gt;P_{i/2} Pi>Pi/2,反过来看就是 P i &lt; P i &lt; &lt; 1 , P i &lt; P i &lt; &lt; 1 ∣ 1 P_i&lt;P_{i&lt;&lt;1},P_i&lt;P_{i&lt;&lt;1|1} Pi<Pi<<1,Pi<Pi<<11,唉这不是个满二叉小根堆吗。

那么问题就变成了点数为 n n n的满二叉小根堆的计数问题, d p [ i ] dp[i] dp[i]表示以 P i P_i Pi为根的满二叉小根堆的个数,有转移方程如下:
d p [ i ] = ( s i z [ i ] − 1 s i z [ i &lt; &lt; 1 ] ) × d p [ i &lt; &lt; 1 ] × d p [ i &lt; &lt; 1 ∣ 1 ] dp[i]=\binom{siz[i]-1}{siz[i&lt;&lt;1]}\times dp[i&lt;&lt;1]\times dp[i&lt;&lt;1|1] dp[i]=(siz[i<<1]siz[i]1)×dp[i<<1]×dp[i<<11]

s i z siz siz数组的话在 d p dp dp时顺带维护一下即可。

由于模数不确定,且一定为质数,所以我们使用 L u c a s \mathcal{Lucas} Lucas定理:
( n m ) ≡ ( ⌊ n p ⌋ ⌊ m p ⌋ ) × ( n m o d &ThinSpace;&ThinSpace; p m m o d &ThinSpace;&ThinSpace; p ) m o d &ThinSpace;&ThinSpace; p \binom{n}{m}\equiv\binom{\lfloor\frac{n}{p}\rfloor}{\lfloor\frac{m}{p}\rfloor}\times \binom{n\mod p}{m\mod p}\mod p (mn)(pmpn)×(mmodpnmodp)modp

代码
#include<bits/stdc++.h>
#define ll long long
#define ls (i<<1)
#define rs (ls|1)
using namespace std;
const int M=1e6+5;
int siz[M],n,mod,mx;
ll fac[M],inv[M],dp[M];
ll power(ll x,ll p){ll r=1;for(;p;p>>=1,x=x*x%mod)if(p&1)r=r*x%mod;return r;}
ll C(int n,int m)
{
    if(m>n)return 0;
    if(n<mod&&m<mod)return fac[n]*inv[n-m]%mod*inv[m]%mod;
    return C(n/mod,m/mod)*C(n%mod,m%mod)%mod;
}
void in(){scanf("%d%d",&n,&mod);}
void ac()
{
    mx=min(mod-1,n);
    fac[0]=1;for(int i=1;i<=mx;++i)fac[i]=fac[i-1]*i%mod;
    inv[mx]=power(fac[mx],mod-2);for(int i=mx-1;i>=0;--i)inv[i]=(i+1)*inv[i+1]%mod;
    for(int i=n;i;--i)
    {
        siz[i]=1;if(ls<=n)siz[i]+=siz[ls];if(rs<=n)siz[i]+=siz[rs];
        if(rs<=n)dp[i]=C(siz[i]-1,siz[ls])*dp[ls]%mod*dp[rs]%mod;
        else if(ls<=n)dp[i]=dp[ls];
        else dp[i]=1;
    }
    printf("%lld",dp[1]);
}
int main()
{
    in(),ac();
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值