【BZOJ2137】submultiple(数论)
【BZOJ2137】submultiple(数论)
题面
题解
首先不难发现答案就是:\(\displaystyle\prod_{i=1}^n (\sum_{j=1}^{p_i+1}j^k)\)。
数据范围给定了。
发现对于\(p_i\)很小的时候,可以直接用快速幂预处理出来,这样子可以做到\(O(n+max(p)*logk)\)的复杂度。
对于\(p\)很大,\(k\)很小的点,不难知道自然数幂和是一个多项式,带几项进去拉格朗日插值或者第二类斯特林数或者带几项高斯消元或者伯努利数或者打表都行。
#include<iostream>
#include<cstdio>
using namespace std;
#define ll long long
#define MOD 1000000007
inline ll read()
{
ll x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int fpow(int a,int b){int s=1;while(b){if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;}return s;}
int n,K,mx,ans=1,P[100100];
namespace Task1
{
int f[100100];
void Solve()
{
for(int i=1;i<=mx;++i)f[i]=(f[i-1]+fpow(i,K))%MOD;
for(int i=1;i<=n;++i)ans=1ll*ans*f[P[i]]%MOD;
}
}
namespace Task2
{
int b[50],a[50],p[50];
void pre()
{
for(int i=1;i<=K+2;++i)p[i]=(p[i-1]+fpow(i,K))%MOD;b[0]=1;
for(int i=0;i<=K+1;++i)
{
for(int j=i+1;j;--j)b[j]=(b[j-1]+MOD-1ll*b[j]*(i+1)%MOD)%MOD;
b[0]=1ll*b[0]*(MOD-i-1)%MOD;
}
for(int i=0;i<=K+1;++i)
{
int s=p[i+1],inv=fpow(i+1,MOD-2);
for(int j=0;j<=K+1;++j)if(i!=j)s=1ll*s*fpow((i-j+MOD)%MOD,MOD-2)%MOD;
b[0]=1ll*b[0]*(MOD-inv)%MOD;
for(int j=1;j<=K+2;++j)b[j]=(MOD-1ll*(b[j]+MOD-b[j-1])*inv%MOD)%MOD;
for(int j=0;j<=K+2;++j)a[j]=(a[j]+1ll*s*b[j])%MOD;
for(int j=K+2;j;--j)b[j]=(b[j-1]+MOD-1ll*b[j]*(i+1)%MOD)%MOD;
b[0]=1ll*b[0]*(MOD-i-1)%MOD;
}
}
int S(int n)
{
int ret=0;
for(int i=0,nw=1;i<=K+1;++i,nw=1ll*nw*n%MOD)
ret=(ret+1ll*a[i]*nw)%MOD;
return ret;
}
void Solve()
{
pre();
for(int i=1;i<=n;++i)ans=1ll*ans*S(P[i])%MOD;
}
}
int main()
{
n=read();K=read();
for(int i=1;i<=n;++i)mx=max(mx,P[i]=read()%MOD+1);
if(mx<=100000)Task1::Solve();
else Task2::Solve();
printf("%d\n",ans);
return 0;
}