On the last day before the famous mathematician Swan's death, he left a problem to the world: Given integers
n
n and
ai
ai for
0≤i≤4
0≤i≤4, calculate the number of
n
n-digit integers which have at most
ai
ai-digit
i
i in its decimal representation (and have no
5,6,7,8
5,6,7,8 or
9
9). Leading zeros are not allowed in this problem.
10 5 0 1 2 3 4 5 1 1 1 1 1 5 2 2 2 2 2 5 3 3 3 3 3 5 3 2 1 3 2 5 3 2 0 0 0 5 0 0 0 5 0 7000 41 2467 6334 2500 3169 7000 7724 3478 5358 2962 464 7000 5705 4145 7281 827 1961
Case #1: 535 Case #2: 96 Case #3: 1776 Case #4: 2416 Case #5: 1460 Case #6: 4 Case #7: 1 Case #8: 459640029 Case #9: 791187801Case #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 1000000007 using 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; } } }