第一场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;
}
posted @ 2021-07-21 22:46  1427314831a  阅读(153)  评论(0编辑  收藏  举报