第一场1004(拉格朗日插值)
2021“MINIEYE杯”中国大学生算法设计超级联赛(1)1004 Another thief in a Shop
A thief made his way to a shop.
There are n kinds of products in the shop and an infinite number of products of each kind. The value of one product of kind i is ai.
The thief wonders how many way to take some products such that the value of them is exactly k (it's possible for some kinds to take several products of that kind).
Find the answer modulo 1e9+7.
\[1\le T\le100\\
1\le n\le100 ,1\le k\le 10^{18},1\le a_i\le 10\\
\]
It is guaranteed that there are no more than 10 testcases with n>50.
It is guaranteed that there are no more than 40 testcases with n>20.
It is guaranteed that there are no more than 80 testcases with n>10.
\[设初始值f[0]=1\\
每加进来一个新的a_i,由低位往高位可以得到递推式f[j]=f[j]+f[j-a[i]]\\
设f[i][j]代表总价值等于i,选取前j件物品时的状态\\
那么有二维的转移关系f[i][j]=f[i][j]+f[i-a[j]][j-1]\\
转移的边界是f[i\%a[j]][j-1]\\
那么f[i][j]只与f[i-a[i]][j-1],f[i-2*a[i]][j-1],\cdots,f[i\%a[j]][j-1]这些状态有关\\
我们要求的答案是a[k][n],那么每一次转移都只与k\%a[j]那一系列项有关,每次转移相当于对这些点做一次前缀和\\
每做一次差分会使多项式的次数-1,相应的,每做一次前缀和会使多项式的系数+1\\
例如初始状态时点值表示为1,0,0,0,0,\cdots\\
做一次前缀和后会变为1,1,1,1,1,\cdots构成只有常数项的0次多项式,或者说y不随x发生变化,即y=1\\
再做一次后会变为1,2,3,4,5,\cdots为一个一次多项式,即y=1+x\\
再做一次后会变为1,3,6,10,15,\cdots可解出一个二次多项式\\
题中一共做了n次前缀和,那么我们就得到了一个n-1次的多项式,需要n个点去唯一确定它\\
那么现在的问题就是找哪n个点去确定这个多项式\\
每次对答案有影响的都是间隔为a[i]的一系列数\\那么我们考虑所有a[i]的lcm这个大区间的每一个点都在之前对答案有影响的点上\\
取连续的n个区间,每个区间都取与k\%lcm有关的一系列点,\\
假设离散化的下标从1开始,那么我们最后取该多项式的第k/lcm+1项即为f[k][n]的值\\
知道n个点去求多项式的某个点考虑用拉格朗日插值去做
\]
#include<bits/stdc++.h>
const int mod=1e9+7;
const int maxn=1e6+3;
using namespace std;
long long n,k;
int bj[maxn];
long long a[100010],f[maxn],inv[maxn],pri[maxn];
long long y[1000010];
long long pre[maxn],per[maxn];
long long BM(long long k)
{
pre[0]=1;per[0]=1;
for(int i=1;i<=n;i++)
{
pre[i]=(k-i)%mod;
per[i]=(k-(n+1-i))%mod;
}
for(int i=1;i<=n;i++)
{
pre[i]=pre[i-1]*pre[i]%mod;
per[i]=per[i-1]*per[i]%mod;
}
long long ans=0;
for(int i=1;i<=n;i++)
{
if((n-i)&1)
ans=(ans-f[i]*pre[i-1]%mod*per[n-i]%mod*inv[i-1]%mod*inv[n-i]%mod+mod)%mod;
else
ans=(ans+f[i]*pre[i-1]%mod*per[n-i]%mod*inv[i-1]%mod*inv[n-i]%mod)%mod;
}
return ans;
}
int gcd(int a,int b)
{
if(b==0)return a;
return gcd(b,a%b);
}
int ksm(int a,int b)
{
int res=1;
while(b)
{
if(b&1)res=1ll*res*a%mod;
a=1ll*a*a%mod;
b>>=1;
}
return res;
}
int main()
{
inv[0]=1;inv[1]=1;
for(int i=2;i<=maxn;i++)
inv[i]=(mod-mod/i)*1ll*inv[mod%i]%mod;
for(int i=2;i<=maxn;i++)
inv[i]=inv[i-1]*inv[i]%mod;
int t;
cin>>t;
while(t--)
{
cin>>n>>k;
int lcm=1;
for(int i=1;i<=n;i++)
{
cin>>a[i];
lcm=lcm*a[i]/gcd(lcm,a[i]);
}
f[0]=1;n++;
for(int i=1;i<=n*lcm;i++)f[i]=0;
for(int j=1;j<=n-1;j++)
for(int i=1;i<=n*lcm;i++)
{
if(i-a[j]>=0)
f[i]=(f[i-a[j]]+f[i])%mod;
}
if(k<=n*lcm)cout<<f[k]<<"\n";
else
{
for(int i=k%lcm;i<n*lcm;i+=lcm)y[i/lcm+1]=f[i];
for(int i=k%lcm;i<n*lcm;i+=lcm)f[i/lcm+1]=y[i/lcm+1];
cout<<BM(k/lcm+1)<<"\n";
}
}
return 0;
}