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;
}