题意
给出由N个正整数组成的数组A,有Q次查询,每个查询包含一个整数K,从数组A中任选K个(K <= N)把他们乘在一起得到一个乘积。求所有不同的方案得到的乘积之和,由于结果巨大,输出Mod 100003的结果即可。例如:1 2 3,从中任选1个共3种方法,{1} {2} {3},和为6。从中任选2个共3种方法,{1 2} {1 3} {2 3},和为2 + 3 + 6 = 11。
Input
第一行:包括2个数N,Q,中间用空格分隔。(1 <= N, Q <= 50000)
第2 至 N + 1行:每行1个数A[i],对应数组A的元素。(1 <= A[i] <= 10^9)
第N + 2 至 N + Q + 1行:每行1个数K。(1 <= K <= N)
Output
输出共Q行,每行1个数,对应每个查询的结果。
题解
首先考虑这个问题可以
dp
,设
f[i]
为选了
i
个数时答案是多少。
方程为
可以发现这是个卷积的形式,但是直接
dp
,没法降低复杂度。
考虑分治,对于一个区间
(l,r)
,求出对于
(l,mid)
的答案和对于
(mid+1,r)
的答案,这样区间
(l,r)
的答案就可以通过两个子区间求出,使用上面的
dp
方程解决,由于这是个卷积,就可以用NTT实现,找两个常用的模数,再用
CRT
合起来,中间注意不要爆
longlong
总复杂度
O(nlg2n)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll GMOD=100003;
ll read(){
ll rt=0;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){rt=rt*10+ch-'0';ch=getchar();}
return rt;
}
void write(ll num){
if(!num)puts("0");
static char str[50];
static int _tmp=0;
while(num){str[++_tmp]=num%10+'0';num/=10;}
while(_tmp){putchar(str[_tmp--]);}
}
ll n,q,data[300000],f[25][300000],sa1[300000],sa2[300000],sb1[300000],sb2[300000];
int R[300000];
ll ksm(ll x,ll y,ll z){
ll ret=1;
while(y){
if(y&1){ret=(x*ret)%z;}
x=(x*x)%z;
y>>=1;
}
return ret;
}
ll msc(ll x,ll y,ll z){
ll ret=0;
while(y){
if(y&1){ret=(ret+x)%z;}
x=(x+x)%z;
y>>=1;
}
return ret;
}
void Init(){
n=read();q=read();
for(int i=1;i<=n;i++)data[i]=read();
}
void Solve(){
while(q--){
write(f[0][read()]);
putchar('\n');
}
}
void NTT(ll *x,ll len,int flag,ll MOD){
for(int i=0;i<len;i++)if(i<R[i])swap(x[i],x[R[i]]);
for(int i=1;i<len;i<<=1){
ll wn=ksm(3,(MOD-1)/((ll)i*2LL),MOD);
for(int j=0;j<len;j+=(i<<1)){
ll w=1;
for(int k=0;k<i;k++,w=(w*wn)%MOD){
ll a=x[j+k],b=(w*x[j+k+i])%MOD;
x[j+k]=(a+b)%MOD;x[j+k+i]=(a-b+MOD)%MOD;
}
}
}
if(flag==-1){
reverse(x+1,x+len);
ll inv=ksm(len,MOD-2,MOD);
for(int i=0;i<len;i++){
x[i]=(x[i]*inv)%MOD;
}
}
}
void DAC(int l,int r,int depth){
for(int i=0;i<=r-l+1;i++)f[depth][i]=0;
if(l==r){
f[depth][0]=1;
f[depth][1]=data[l]%GMOD;
return;
}
int mid=(l+r)>>1;
DAC(l,mid,depth+1);
for(int i=0;i<=r-l+1;i++)f[depth][i]=f[depth+1][i];
DAC(mid+1,r,depth+1);
int m=r-l+1,L=0,tmp=0;
for(tmp=1;tmp<=m;tmp<<=1)L++;
for(int i=0;i<tmp;i++)R[i]=0;
for(int i=0;i<tmp;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
for(int i=r-(mid)+1;i<tmp;i++)f[depth+1][i]=0;
for(int i=mid+1-l+1;i<tmp;i++)f[depth][i]=0;
for(int i=0;i<tmp;i++){
sa1[i]=sa2[i]=f[depth][i];
sb1[i]=sb2[i]=f[depth+1][i];
}
NTT(sa1,tmp,1,998244353LL);NTT(sb1,tmp,1,998244353LL);
for(int i=0;i<tmp;i++)sa1[i]=(sa1[i]*sb1[i])%998244353LL;
NTT(sa1,tmp,-1,998244353LL);
NTT(sa2,tmp,1,1004535809LL);NTT(sb2,tmp,1,1004535809LL);
for(int i=0;i<tmp;i++)sa2[i]=(sa2[i]*sb2[i])%1004535809LL;
NTT(sa2,tmp,-1,1004535809LL);
ll inv1=ksm(1004535809LL,998244353LL-2LL,998244353LL);
ll inv2=ksm(998244353LL,1004535809LL-2LL,1004535809LL);
ll M=998244353LL*1004535809LL;
for(int i=0;i<=m;i++){
f[depth][i]=(sa1[i]*1004535809LL)%M;
f[depth][i]=msc(f[depth][i],inv1,M);
ll ttt=(sa2[i]*998244353LL)%M;
ttt=msc(ttt,inv2,M);
f[depth][i]=(f[depth][i]+ttt)%M;
f[depth][i]+=GMOD;
f[depth][i]%=GMOD;
}
}
int main(){
Init();
DAC(1,n,0);
Solve();
return 0;
}
其实这道题不需要 NTT , FFT 就可以了。