HDU 5519 Kykneion asma

题目链接

题目大意

给定一个 n n 和一个序列 ai(0i4) a i ( 0 ≤ i ≤ 4 ) 求满足下列条件的 n n 位数个数。

  • 没有 5.6.7.8.9 5.6.7.8.9
  • 0.1.2.3.4 0.1.2.3.4 的个数不超过 ai a i

109+7 10 9 + 7 取模

思路

感觉很像是生成函数题。
但是据说生成函数做法需要FFT。weila

那就换一种方式考虑。考虑容斥。
用总的方案数减去有一个数超的方案数加上两个数超的方案数
那么怎么求这有些数超的方案数呢?考虑DP。
f[i][j][S] f [ i ] [ j ] [ S ] 表示前 i i 个数, S S 状态对应的数都超了,有 j j 个数最后要超,可以有前导零的方案数。
转移: f[i][j][S]=f[i1][j][S](5j+__builtin_popcount(S))+S(1<<k)f[ia[k]1][j][S(1<<k)](i1a[k]) f [ i ] [ j ] [ S ] = f [ i − 1 ] [ j ] [ S ] ∗ ( 5 − j + _ _ b u i l t i n _ p o p c o u n t ( S ) ) + ∑ S − ( 1 << k ) f [ i − a [ k ] − 1 ] [ j ] [ S − ( 1 << k ) ] ∗ ( i − 1 a [ k ] )
最后处理前导零:先对原来的 a a 数组做一遍,再将 a[0] a [ 0 ] − − ,再做一遍,减去这第二次做的即可。

代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define pa pair
#define INF 0x3f3f3f3f
#define inf 0x3f
#define fi first
#define se second
#define mp make_pair
#define ll long long
#define ull unsigned long long
#define pb push_back

using namespace std;

inline ll read()
{
    long long f=1,sum=0;
    char c=getchar();
    while (!isdigit(c)) {if (c=='-') f=-1;c=getchar();}
    while (isdigit(c)) {sum=sum*10+c-'0';c=getchar();}
    return sum*f;
}
const int MAXN=15010;
ll f[MAXN][6][50];
int a[5];
#define Mod 1000000007
ll quickpow(ll a,ll b)
{
    ll ans=1;
    while (b)
    {
        if (b&1) ans*=a,ans%=Mod;
        b/=2,a*=a,a%=Mod;
    }
    return ans;
}
ll inv[MAXN],fac[MAXN];
ll C(int n,int m)
{
    return fac[n]*inv[m]%Mod*inv[n-m]%Mod;
}
int num[1<<6]; 
ll work(int n)
{
    memset(f,0,sizeof(f));
    for (int i=1;i<=5;i++)  
        f[0][i][0]=1;
    for (int j=1;j<=5;j++)
    {
        for (int i=1;i<=n;i++)
        {
            for (int s=0;s<1<<5;s++)
            {
                if (num[s]>j) continue;
                f[i][j][s]=f[i-1][j][s]*(5-j+num[s])%Mod;
                for (int k=1;k<=5;k++)
                {
                    if ((1<<(k-1)&s) && (i>=a[k]+1))
                        f[i][j][s]=(f[i][j][s]+f[i-a[k]-1][j][s-(1<<(k-1))]*C(i-1,a[k]))%Mod;
                } 
            }
        }
    }
    ll ans=quickpow(5,n);
    for (int s=1;s<1<<5;s++)
    {
        if (num[s]&1) ans=(ans-f[n][num[s]][s]+Mod)%Mod;
        else ans=(ans+f[n][num[s]][s]+Mod)%Mod;
    }
    return ans;
}
int main()
{
    int T;
    scanf("%d",&T);
    fac[0]=1;
    for (int i=1;i1]*i%Mod;
    inv[MAXN-1]=quickpow(fac[MAXN-1],Mod-2);
    for (int i=MAXN-2;i>=0;i--)
        inv[i]=inv[i+1]*(i+1)%Mod;
    for (int i=0;i<1<<5;i++)
        for (int x=i;x;num[i]+=(x&1),x/=2);
    for (int t=1;t<=T;t++) 
    {
        printf("Case #%d: ",t);
        int n;
        scanf("%d",&n);
        for (int i=1;i<=5;i++)
            scanf("%d",&a[i]);
        if (!a[1])
            printf("%d\n",work(n));
        else
        {
            ll ans=work(n);
            a[1]--;
            ans=(ans-work(n-1)+Mod)%Mod;
            printf("%d\n",ans);
        }
    }
}

你可能感兴趣的:(DP,数学)