Description:
经典问题,给你
N(N<=50)
种面值的硬币,每种可以使用无限次,第
i
硬币的面值为
Solution:
首先对于面值为 Di 的硬币构造多项式:
Fi=∑p=0+∞xDi∗p
令
G(x)=∏i=1NFi
那么显然把 G(x) 中 xC 项的系数就是答案。
但是此题 C 的范围异常的大,这让我们不能直接计算乘法之后的多项式,得考虑别的算法。
Fi=11−xDi
所以
G(x)=∏i=1N11−xDi=1(1−x)N∏i=1N11+x+...+xDi−1
把 G(x) 分式分解得:
G(x)=A(x)(1−x)N+∑i=1NBi(x)1+x+...+xDi−1=∏i=1N11−xDi
乱证明一下发现 A(x) 是一个次数小于 N 的多项式,
考虑多项式 Bi(x) ,那么有
Bi(x)1+x+...+xDi−1=∏j=1N11−xDj−A(x)(1−x)N−∑j=1,j!=iNBj(x)1+x+...+xDj−1
令
Ri(x)=A(x)(1−x)N+∑j=1,j!=iNBj(x)1+x+...+xDj−1
那么
Bi(x)1+x+...+xDi−1=∏j=1N11−xDj−Ri(x)
将上式两边乘以左边分母有:
Bi(x)=∏j=1N11−xDj×(1+x+...+xDj−1)−Ri(x)×(1+x+...+xDi−1)
=1(1−x)∏Nj=1,j!=i(1−xDj)−Ri(x)×(1+x+...+xDi−1)
那么对于 x=ωtDi (1<=t<Di) ,根据等比数列求和, 1+x+...+xDi−1=0 ,因为所有的 Di 互质,当 x=ωtDi 时, Ri(x)×(1+x+...+xDi−1)=0
所以
Bi(ωtDi)=1(1−ωtDi)∏Nj=1,j!=i(1−ωt×DjDi)
考虑使用等比数列求和可以十分容易的证明下式 (j!=0) :
−1Di×∑p=0Di−1(p+1)∗ω p×jDi=11−ω jDi
所以
Bi(ωtDi)=(−1Di)N×(∑p=0Di−1(p+1)×ω p×tDi)×∏j=1,j!=iN(∑p=0Di−1(p+1)×ω p×t×DjDi)
令
Bi(ωtDi)=(−1Di)N×∑j=0Di−1bj×ωjDi
如果要求出 bi ,那么需要做 N 次形如下式的卷积运算(以下所有的指数和下标运算都是在模
(∑p=0Di−1apωpDi)×(∑p=0Di−1(p+1)ωp×qDi)=∑p=0Di−1cpωpDi
cp=∑k=0Di−1(k+1)×ap−q×k=∑k=0Di−1k×ap−q×k+∑k=0Di−1ak=∑k=0Di−2(k+1)×ap−q×(k+1)+∑k=0Di−1ak
=∑k=0Di−2(k+1)×a(p−q)−q×k+∑k=0Di−1ak=∑k=0Di−1(k+1)×a(p−q)−q×k+∑k=0Di−1ak−Di×a(p−q)−q×(Di−1)
=cp−q+∑k=0Di−1ak−Di×a(p−q)−q×(Di−1)
所以对于一次卷积运算,可以先求出 c0 ,通过不断的将下标加 q 求出下一个
观察发现,对于不同的 t
r0,r1,...,rDi−1 都是相同的,对应等于 t=1 时的 b0,b1,...,bDi−1 。
由于 Bi(x) 是一个 Di−2 次多项式,且对于 Bi(x) 当 x=ωtDi(t=1,2..,Di−1) 时都满足
Bi(ωtDi)=∑p=0Di−2(rp−rDi−1)×(ωtDi)p
那么断定
Bi(x)=∑p=0Di−2(rp−rDi−1)×xp
考虑
Bi(x)1+x+...+xDi−1
对答案的贡献。
Bi(x)1+x+...+xDi−1=Bi(x)×(1−x)1−xDi=Bi(x)×(1−x)×∑p=0+∞xDi×p
由于 Bi(x) 是一个 Di−2 次多项式 ,显然 Bi(x)1+x+...+xDi−1 中 xDi×u+v(0<=u,0<=v<Di) 的系数为 (rv−rDi−1)−(rv−1−rDi−1)=rv−rv−1
所以把 C 对于
所以求出所有 Di(x) 对答案的贡献的时间复杂度是 O(N2×D)
之后 A(x)(1−x)N 对答案的贡献,乱证明一下发现 A(x) 是一个多项式,并且每一项的系数都是常数。
并且
1(1−x)N=∑i=0+∞h(i)xi
其中 h(x) 是一个关于 x 的
又因为 A(x) 是一个系数为常数的多项式,所以可以断定
A(x)(1−x)N=∑i=0+∞S(i)xi
其中 S(x) 是一个关于 x 的
由于我们能够求出 Bi(x) 对于答案的贡献,并且对于 C 比较小的时候的答案我们是可以
所以先用 dp 求出 C=0,1,2,...,N−1 时的答案,然后求出 Bi(x) 的贡献,那么就可以求出 S(0),S(1),...,S(N−1) ,然后插值或者高斯消元可以解出多项式 S(x) 各项的系数。之后就得出了 S(x) 的表达式 。之后计算对于题目给出的 C 的答案,
至此此题终结。
Code:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std;
const long long Mod=1e9+7;
int N;
int T;
int D[60]={0};
long long B[60][510]={{0}};
long long help[510]={0};
long long A[110]={0};
long long F[110]={0};
long long fc[110][110]={{0}};
struct Bignum
{
int cnmbdctr[200];
}C={0};
void read_(struct Bignum &R)
{
int len=0;
char ch=getchar();
for(;ch<'0' || ch>'9';ch=getchar());
for(;ch>='0' && ch<='9';ch=getchar())
R.cnmbdctr[++len]=ch-'0';
for(int i=(len>>1);i>=1;i--)
swap(R.cnmbdctr[i],R.cnmbdctr[len-i+1]);
R.cnmbdctr[0]=len;
return;
}
int Module(Bignum R,long long MM)
{
long long remains=0;
for(int i=R.cnmbdctr[0];i>=1;i--)
remains=(remains*10+R.cnmbdctr[i])%MM;
return (int)remains;
}
long long power(long long a,long long k)
{
long long o=1;
for(;k>0;k>>=1)
{
if(k&1) o=o*a%Mod;
a=a*a%Mod;
}
return o;
}
void Guass()
{
for(int i=0;i<=N;i++)
{
int gg=i;
for(;gg<=N;gg++)
if(fc[gg][i]!=0) break;
if(gg==N+1) continue;
swap(fc[i],fc[gg]);
long long Inv=power(fc[gg][i],Mod-2);
for(int j=i;j<=N+1;j++)
fc[gg][j]=fc[gg][j]*Inv%Mod;
for(int j=0;j<=N;j++)
{
if(j==i) continue;
if(fc[j][i]!=0)
{
long long mult=fc[j][i];
for(int p=i;p<=N+1;p++)
fc[j][p]=(fc[j][p]-fc[gg][p]*mult%Mod+Mod)%Mod;
}
}
}
return;
}
long long getansb(int i,int PP)
{
if(PP==0)
return (B[i][0]-B[i][D[i]-1]+Mod)%Mod;
else return (B[i][PP]-B[i][PP-1]+Mod)%Mod;
}
int main()
{
cin>>T;
for(;T>0;T--)
{
cin>>N;
memset(C.cnmbdctr,0,sizeof(C.cnmbdctr));
read_(C);
for(int i=1;i<=N;i++)
scanf("%d",&D[i]);
memset(B,0,sizeof(B));
memset(help,0,sizeof(help));
for(int i=1;i<=N;i++)
{
long long Inv=power(Mod-D[i],Mod-2);
Inv=power(Inv,N);
for(int j=0;j<D[i];j++)
B[i][j]=j+1;
for(int p=1;p<=N;p++)
{
if(p==i) continue;
long long Sum=0;
for(int j=0;j<D[i];j++)
{
help[j]=B[i][j];
B[i][j]=0;
Sum=(Sum+help[j])%Mod;
}
for(int j=0;j<D[i];j++)
B[i][0]=(B[i][0]+(j+1)*help[(D[i]-j*D[p]%D[i])%D[i]])%Mod;
int Cnt=0;
for(int j=1;j<D[i];j++)
{
int Next=(Cnt+D[p])%D[i];
B[i][Next]=(B[i][Cnt]+Sum-(D[i]*help[(Cnt-D[p]*(D[i]-1)%D[i]+D[i])%D[i]])%Mod+Mod)%Mod;
Cnt=Next;
}
}
for(int j=0;j<D[i];j++)
B[i][j]=B[i][j]*Inv%Mod;
}
memset(F,0,sizeof(F));
F[0]=1;
for(int i=1;i<=N;i++)
{
for(int j=D[i];j<=N+20;j++)
F[j]=(F[j]+F[j-D[i]])%Mod;
}
memset(fc,0,sizeof(fc));
for(int i=0;i<=N;i++)
{
fc[i][0]=1;
long long Cnt=i;
for(int j=1;j<=N;j++)
{
fc[i][j]=Cnt;
Cnt=Cnt*i%Mod;
}
fc[i][N+1]=F[i];
for(int j=1;j<=N;j++)
fc[i][N+1]=(fc[i][N+1]-getansb(j,i%D[j])+Mod)%Mod;
}
Guass();
long long Ans=0;
for(int i=1;i<=N;i++)
{
int PP=Module(C,(long long)D[i]);
Ans=(Ans+getansb(i,PP))%Mod;
}
long long Cnt=1;
for(int j=0;j<=N;j++)
{
Ans=(Ans+fc[j][N+1]*Cnt)%Mod;
Cnt=Cnt*Module(C,Mod)%Mod;
}
cout<<Ans<<endl;
}
return 0;
}