Perm 排列计数

题目描述

称一个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

样例输出

16

数据范围与提示

100%的数据中,1 ≤ ??? N ≤ 106, P??? ≤ 10^9,p是一个质数。 数据有所加强

 

刚刚学了组合数学,然后就水了道题。

模板一定要好好学,一个取模颓老半天。。。。。

然后开始说正经的

首先这题仔细想想可以看成二叉堆(满足大小关系嘛。。。)

然后就可以像个树规似的

从最下的节点看是向上转移

设f数组表示方案数size数组表示子树大小

因为每个节点的左子树的方案树与右子树方案数相乘并没有枚举出全部结果

仔细想想每个节点的左右子树的数是可以互换的这样依旧满足二叉堆性质

C(size[i]-1,size[left])可表示

然后数据范围可以用卢卡斯定理取模

这就不解释了反正网上很多。。。。

C(n,m)%p=C(n%p,m%p)*C(n/p,m/p)%p

总之最后转移的式子

f[x]=f[x*2]*f[x*2+1]*C(size[x]-1,size[x*2])

蒟蒻第一次写博客如有不当请指正

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cmath>
 4 #include<algorithm>
 5 #include<cstring>
 6 #define MAXN 2006001
 7 #define ll long long
 8 using namespace std;
 9 ll sum[MAXN];
10 ll size[MAXN],f[MAXN];
11 ll n,p;
12 ll pow(ll x,ll y)
13 {
14     ll ans=1;
15     while(y>0)
16     {
17         if(y&1)ans=ans*x%p;
18         x=x*x%p;
19         y>>=1;
20     }
21     return ans%p;
22 }
23 ll getsum(ll x,ll y)
24 {
25    if(y>x) return 0; 
26    if(!y)return 1;
27    return (sum[x]*pow(sum[y]*sum[x-y]%p,p-2)%p)%p;
28 }
29 ll lucas(ll x,ll y)
30 {
31    if(y>x)return 0;
32    if(y==0)return 1;
33    if(x>p||y>p)return lucas(x/p,y/p)*getsum(x%p,y%p)%p;
34    return getsum(x,y)%p;
35 }
36 void find(ll k)
37 { 
38     if(k>n){f[k]=1;return ;}
39     find(k*2);find(k*2+1);
40     size[k]=size[k*2]+size[k*2+1]+1;
41     f[k]=f[k*2]*f[k*2+1]%p*lucas(size[k]-1,size[k*2])%p;
42 }
43 int main()
44 {
45      scanf("%lld%lld",&n,&p);
46      sum[0]=1;sum[1]=1;
47      for(ll i=2;i<=n;++i)sum[i]=((sum[i-1]*1ll*i)%p);
48      find(1);
49      printf("%lld
",f[1]%p);
50 }
View Code

 

原文地址:https://www.cnblogs.com/Wwb123/p/11119667.html