Hdu-5519 Kykneion asma(状压DP+容斥)

来源:互联网 发布:西安电子大学网络教育 编辑:程序博客网 时间:2024/06/07 07:26
On the last day before the famous mathematician Swan's death, he left a problem to the world: Given integers nn and aiai for 0i40≤i≤4, calculate the number of nn-digit integers which have at most aiai-digit ii in its decimal representation (and have no 5,6,7,85,6,7,8 or 99). Leading zeros are not allowed in this problem.
Input
There is one integer T (1<T10)T (1<T≤10) in the beginning of input, which means that you need to process TT test cases. In each test case, there is one line containing six integers representing nn and a0a0 to a4a4, where 2n150002≤n≤15000 and 0ai300000≤ai≤30000.
Output
For each test case, you should print first the identifier of the test case and then the answer to the problem, module 109+7109+7.
Sample Input
105 0 1 2 3 45 1 1 1 1 15 2 2 2 2 25 3 3 3 3 35 3 2 1 3 25 3 2 0 0 05 0 0 0 5 07000 41 2467 6334 2500 31697000 7724 3478 5358 2962 4647000 5705 4145 7281 827 1961
Sample Output
Case #1: 535Case #2: 96Case #3: 1776Case #4: 2416Case #5: 1460Case #6: 4Case #7: 1Case #8: 459640029Case #9: 791187801

Case #10: 526649529

题意: 给你包括0的5个数字,又告诉你每个数字的最多使用次数,问你由这5个数字组成的不同n位数(无前导0)有多少个。

分析:这题可以直接上母函数+FFT,但是不能直接用NTT因为模数不是费马素数,然后在网上看到了还有这种容斥+状压DP的做法,容斥很好想,但是难点在怎么用状压DP求给定数字集超出限制的方案数,这里有一个小trick就是如果我们想让某个数字i超出限制,那么直接放入a[i]+1个数字就行了,f[i][mask][j]表示当前在第i位,当前 mask 这些数字超出了限制,且最终超出限制的数字为j个的方案数,那么有:

f[i][mask][j] = f[i-1][mask][j]*(5 - j + count(mask))+sigma(f[i-a[k]-1][mask xor (1<<k)][j]*C(i-1,a[k]))

加上f[i-1][mask][j]这部分很容易理解,相当于枚举第i位的数字,右边的部分相当于在此刻刚好超过a[k]这个限制时的方案数。

#include <bits/stdc++.h>#define MOD 1000000007using namespace std;typedef long long ll;int T,Time,n,cnt[32],a[6];ll jc[15005],inv[15005],f[15005][32][6];void exgcd(ll a,ll b,ll &g,ll &x,ll &y){    if(!b) g=a,x=1,y=0;    else    {        exgcd(b,a%b,g,y,x);        y-=a/b*x;    }}ll Inv(ll a,ll n){    ll d,x,y;    exgcd(a,n,d,x,y);    return d == 1 ? (x+n)%n : -1;}int lowbit(int x){    return x & -x;}ll c(int x,int y){    return (jc[x]*inv[y] % MOD)*inv[x-y] % MOD;}void add(ll &x,ll y){    y %= MOD;    x = (x + y) % MOD;}ll got(int n){    memset(f,0,sizeof(f));    for(int i = 1;i <= 5;i++) f[0][0][i] = 1;    for(int j = 1;j <= 5;j++)     for(int i = 1;i <= n;i++)      for(int mask = 0;mask < 32;mask++)      if(cnt[mask] <= j)      {         add(f[i][mask][j],f[i-1][mask][j] * (5 - j + cnt[mask]));         for(int k = 1;k <= 5;k++)         if(((1<<(k-1)) & mask) && i >= a[k] + 1) add(f[i][mask][j],f[i - a[k] - 1][mask - (1<<(k-1))][j]*c(i-1,a[k]));      }    ll temp = 1;    for(int i = 1;i <= n;i++) temp = temp*5 % MOD;    for(int mask = 1;mask < 32;mask++)     if(cnt[mask] & 1) temp = (temp - f[n][mask][cnt[mask]] + MOD) % MOD;     else temp = (temp + f[n][mask][cnt[mask]]) % MOD;    return temp;}int main(){    jc[0] = inv[0] = 1;    for(int i = 1;i <= 15000;i++) jc[i] = jc[i-1] * i % MOD,inv[i] = Inv(jc[i],MOD);    for(int i = 1;i < 32;i++) cnt[i] = cnt[i - lowbit(i)] + 1;    cin.sync_with_stdio(false);    cin>>T;    while(T--)    {        cin>>n;        for(int i = 1;i <= 5;i++) cin>>a[i];        if(!a[1]) cout<<"Case #"<<++Time<<": "<<got(n)<<endl;        else        {            int temp = got(n);            a[1]--;            temp = (temp - got(n-1) + MOD) % MOD;            cout<<"Case #"<<++Time<<": "<<temp<<endl;        }    }}



原创粉丝点击