HDU5519-数位DP或者FFT

来源:互联网 发布:天行健网络 编辑:程序博客网 时间:2024/06/05 19:23

https://vjudge.net/contest/166969#problem/K
FFT 本渣渣是真不会。但是目标是要学会它。不要求具体会,会用板子就行。
用的数位Dp。
给定 0 1 2 3 4 这5个数,以及他们能出现的个数,问你用这些数最多能够成多少个不含前导0的n位数。

#include <iostream>#include <cstdio>#include <cstring>#define mod 1000000007#define MAXN 15005#define LL long long intusing namespace std;const int END=1<<5;int n, dp[MAXN][END+5], a[10];LL fac[MAXN], ni[MAXN], cnt[END+5];LL quickpow(LL m,LL n){    int b=1;    while(n>0)    {        if(n&1)b=(b*m)%mod;        n=n>>1;        m=(m*m)%mod;    }return b;}int get(LL n){    int ans=0;    while(n>0)    {        if(n&1)            ans++;        n>>=1;    }    return ans;}void init(){    fac[0]=fac[1]=ni[0]=ni[1]=1;    for(int i=2;i<=15000;++i)    {        fac[i]=fac[i-1]*i%mod;        ni[i]=quickpow(fac[i],1000000005);    }    //cnt[i]表示i中有几个1    for(int i=1;i<END;++i)cnt[i]=get(i);}LL c(int n,int m){return fac[n]*ni[m]%mod*ni[n-m]%mod;}int solve(){    memset(dp,0,sizeof dp);    for(int s=0;s<END;++s)        if(cnt[s^(END-1)]&1)dp[0][s]=-1;        else dp[0][s]=1;    for(int i=1;i<=n;++i)    {        for(int s=0;s<END;++s)        {            dp[i][s]=dp[i][s]+dp[i-1][s]*cnt[s];             for(int k=0;k<5;++k)                if(!((s>>k)&1)&&a[k]<i)                dp[i][s|(1<<k)]=dp[i][s|(1<<k)]+dp[i-1-a[k]][s]*c(i-1,a[k]);        }    }    return dp[n][END-1];}int work(){    int ans=solve();    if(a[0]>0)    {        --n, --a[0];        ans=ans-solve();    }    return ans;}int main(){    init();    int t, cas=0;    scanf("%d",&t);    while(t--)    {        scanf("%d",&n);        for(int i=0;i<5;++i)scanf("%d",&a[i]);        printf("Case #%d: %d\n",++cas,work());    }    return 0;}
原创粉丝点击