【bzoj2111】【zjoi2010】【perm排列计数】【dp+Lucas定理】

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

Sample Output

16

HINT

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

题解:分析一下题目会发现实际上是要求能形成的小根堆的数量。

           然后类似树形dp那样线性的搞一下就好了。

           设f[i]为互不相同的i个数能形成的小根堆的数量。则f[i]=c(i-1,x)*f[x]*f[i-1-x];

           因为这是一颗完全二叉树,所以可以由父节点推出子节点的编号,顺便维护一下子树大小。

           然后转移就O(1)了。

           因为p比较大,所以需要使用Lucas定理来求组合数。

#include<iostream>
#include<cstdio>
#define N 3000010
using namespace std;
long long f[N],s[N],p;
int n;
inline long long power(long long a,long long b)
{
	long long ans;
	for(ans=1;b;b>>=1,a=a*a%p) if(b&1) ans=ans*a%p;
	return ans;
}
inline long long cal(long long n,long long m)
{
	long long s1(1),s2(1);
	if (n<m) return 0;
	if (m>n-m) m=n-m;
	for (long long i=0;i<m;i++)
	 {
	 	(s1*=n-i)%=p;
	 	(s2*=i+1)%=p;
	 }
	return s1*power(s2,p-2)%p;
}
inline long long lucas(long long n,long long m)
{
	if (m==0) return 1;
	else return cal(n%p,m%p)*lucas(n/p,m/p)%p;
}
int main()
{
   scanf("%d%lld",&n,&p);
   for (int i=n;i;i--)
    {
       s[i]=s[i<<1]+s[i<<1|1]+1;
	   f[i]=lucas(s[i]-1,s[i<<1]);
	   if ((i<<1)<=n) (f[i]*=f[i<<1])%=p;
	   if ((i<<1|1)<=n) (f[i]*=f[i<<1|1])%=p;	
    }
   cout<<f[1]<<endl;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值