BZOJ 2111: [ZJOI2010]Perm 排列计数 DP+lucas定理

来源:互联网 发布:老司机网址知乎 编辑:程序博客网 时间:2024/05/29 13:44

时空隧道


分析:
盯着Pi>Pi/2这个条件看了很久觉得很眼熟…但是就是想不出来…
后来搜了一下题解…发现woc这不就是小根堆…
这就很简单了…DP就好了…
f[i]=f[l]*f[r]*C(i-1,l) (f[i]代表的是有i个节点的方案数)
但是由于n可能大于MOD,所以要用到lucas定理…
C(n,m)=C(n/MOD,m/MOD)*C(n%MOD,m%MOD)


代码如下:

#include<algorithm>#include<iostream>#include<cstring>#include<cstdio>//by NeighThorn#define int long long //飘风回而起闰兮,举帷幄之襜襜using namespace std;const int maxn=1000000+5;int n,f[maxn],MOD;long long factorial[maxn],Pow[30+5];inline int calcl(int x){    int i,tmp=x;    for(i=0;i<=30&&x>=Pow[i];i++)        x-=Pow[i];    if(x==0)        return (tmp-1)/2;    else if(x>=Pow[i-1])        return Pow[i]-1;    else                return Pow[i-1]+x-1;}inline int calcr(int x){    int i,tmp=x;    for(i=0;i<=30&&x>=Pow[i];i++)        x-=Pow[i];    if(x==0)        return (tmp-1)/2;    else if(x>=Pow[i-1])        return x-1;     else        return Pow[i-1]-1;}inline int power(int x,int y){    long long ans=1;    while(y){        if(y&1)            ans=ans*(long long)x%MOD;        x=(long long)x*(long long)x%MOD,y>>=1;    }    return ans;}inline int calc(int x,int y){    return (long long)factorial[y]*(long long)power(factorial[y-x],MOD-2)%MOD*(long long)power(factorial[x],MOD-2)%MOD;}inline int C(int x,int y){    return y==0?1:(long long)C(x/MOD,y/MOD)*(long long)calc(x%MOD,y%MOD)%MOD;}signed main(void){    scanf("%lld%lld",&n,&MOD);factorial[0]=1;Pow[0]=1;    for(int i=1;i<=n;i++)        factorial[i]=factorial[i-1]*i%MOD;    for(int i=1;i<=30;i++)        Pow[i]=Pow[i-1]<<1;    f[1]=1;f[0]=1;    for(int i=2,x;i<=n;i++)        f[i]=f[x=calcl(i)]*f[calcr(i)]%MOD*C(x,i-1)%MOD;    cout<<f[n]<<endl;    return 0;}

by >_< NeighThorn

1 0
原创粉丝点击