这题想了好久没想出来,最后是学习的这里的做法解决的:K题题解
基于FFT,本弱到现在还是不会写,所以并不能提供FFT的写法。
分析:设 W(n,a0,a1,a2,a3,a4) 代表可以有前导 0 的 n 位 5 进制数字,至多 ai 个数字 i 的方案数。那么
设 S(ab1,ab2..,abm) 代表数 b1,b2,..,bm 一定超过限制的方案数。
dp[i][msk][MSK] 代表最终有状态 MSK 的数一定超过限制,当前 i 个数,有 msk 个数超过限制,这里可以 msk 是 MSK 的子状态。
注意点:这里 MSK 中存在的数, msk 不存在的数,不能填入 i 位。
设 cnt[msk] 代表这个状态下 1 的个数
这样得到
这里有一个东西: MSK 这个状态在表达式中只提供了一个 1 的个数,这个时候把 MSK 直接定义成最终有多少个数字超过了限制,最后一维设成 j :
此时:
此时复杂度为 o(4∗n∗25∗52)
附上代码:
#include
#include
#define LL long long
#define FOR(i,x,y) for(int i = x;i < y;++ i)
#define IFOR(i,x,y) for(int i = x;i < y;-- i)
using namespace std;
typedef vector <int> VT;
const int maxn = 15010;
const int maxm = 30030;
const int Mod = 1e9+7;
const int MSK = 1<<5;
int n,a[5];
void gcd(LL a,LL b,LL& d,LL& x,LL& y){
if(!b) {d = a;x = 1;y = 0;}
else{gcd(b,a%b,d,y,x);y -= x*(a/b);}
}
LL Inv(LL a,LL n){
LL d,x,y;
gcd(a,n,d,x,y);
return d == 1 ? (x+n)%n : -1;
}
LL fac[maxn],inv[maxn],cnt[MSK];
void init(){
fac[0] = 1; inv[0] = 1;
FOR(i,1,maxn){
fac[i] = i*fac[i-1]%Mod;
LL p = Inv(i,Mod);
inv[i] = inv[i-1]*p%Mod;
}
FOR(i,0,MSK) cnt[i] = __builtin_popcount(i);
}
LL dp[maxn][MSK];
LL Pow;
LL C(LL p,LL q){
return fac[p]*inv[q]%Mod*inv[p-q]%Mod;
}
LL quickpow(LL a,LL n,LL m){
LL ans=1;
while(n){
if(n&1) ans = (ans*a)%m;
a = (a*a)%m;
n>>=1;
}
return ans;
}
void work(){
LL ans = 0;
FOR(p,1,5){
if(!a[p]) continue;
ans += Pow;
ans %= Mod;
-- a[p];
-- n;
FOR(j,1,6){
memset(dp,0,sizeof(dp));
dp[0][0] = 1;
FOR(i,1,n+1){
FOR(msk,0,MSK){
if(cnt[msk] <= j){
dp[i][msk] = dp[i-1][msk]*(5-j+cnt[msk])%Mod;
FOR(k,0,5){
if((msk & (1<1){
dp[i][msk] += dp[i-a[k]-1][msk^(1<1,a[k]);
dp[i][msk] %= Mod;
}
}
}
}
}
FOR(msk,0,MSK){
if(cnt[msk] == j){
if(j % 2){
ans -= dp[n][msk];
ans = (ans + Mod)%Mod;
}
else{
ans += dp[n][msk];
ans %= Mod;
}
}
}
}
++ n;
++ a[p];
}
printf("%I64d\n",ans);
}
int main()
{
//freopen("test.in","r",stdin);
init();
int T,tCase = 0; scanf("%d",&T);
while(T--){
printf("Case #%d: ",++tCase);
scanf("%d",&n);
FOR(i,0,5) scanf("%d",&a[i]);
Pow = quickpow(5,n-1,Mod);
work();
}
return 0;
}