题目大意
给定一个 n n 和一个序列求满足下列条件的 n n 位数个数。
- 没有
- 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 个数,状态对应的数都超了,有 j j 个数最后要超,可以有前导零的方案数。
转移:
最后处理前导零:先对原来的 a a 数组做一遍,再将,再做一遍,减去这第二次做的即可。代码
#include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<iostream> #include<cmath> #include<cstdlib> #include<ctime> #include<map> #include<queue> #include<vector> #include<stack> #include<set> #include<cctype> #define pa pair<int,int> #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;i<MAXN;i++) fac[i]=fac[i-1]*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); } } }