bzoj 2111: [ZJOI2010]Perm 排列计数【树形dp+lucas】

是我想复杂了
首先发现大于关系构成了一棵二叉树的结构,于是树形dp
设f[i]为i点的方案数,si[i]为i点的子树大小,递推式是\( f[i]=f[i*2]*f[i*2+1]*C_{si[i]-1}^{si[i*2]} \)
组合数用Lucas求

#include<iostream>
#include<cstdio>
using namespace std;
long long n,p,f[5000005],jc[5000005],s[5000005];
long long ksm(long long a,long long b)
{
    long long r=1;
    while(b)
    {
        if(b&1)
			r=r*a%p;
        a=a*a%p;
        b>>=1;
    }
    return r;
}
long long C(long long x,long long y)
{
    if(y>x)
		return 0;
    return jc[x]*ksm(jc[y]*jc[x-y]%p,p-2)%p;
}
long long luc(long long x,long long y)
{
    if(!y)
		return 1;
    return C(x%p,y%p)*luc(x/p,y/p)%p;
}
void dfs(long long u)
{
    s[u]=1;f[u]=1;f[u<<1]=1;f[u<<1|1]=1;
    if((u<<1)<=n)
		dfs(u<<1),s[u]+=s[u<<1];
    if((u<<1|1)<=n)
		dfs(u<<1|1),s[u]+=s[u<<1|1];
    f[u]=f[u<<1]*f[u<<1|1]%p*luc(s[u]-1,s[u<<1])%p;
}
int main()
{
    scanf("%lld%lld",&n,&p);
    jc[0]=jc[1]=1;
    for(long long i=2;i<=n;i++)
		jc[i]=jc[i-1]*i%p;
    dfs(1);
    printf("%lld",f[1]);
    return 0;
} 
posted @ 2018-03-08 19:59  lokiii  阅读(120)  评论(0编辑  收藏  举报