51nod1348乘积之和

Posted sadstone

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51nod1348乘积之和相关的知识,希望对你有一定的参考价值。

Description

给出由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。

Solution

(f_{l,r,x}(0leq xleq r-l+1))表示区间([l,r])选了(x)个的乘积和,可以发现(f_{l,r})(f_{l,p})(f_{p+1,r}(lleq p<r))的卷积。
直接分治(NTT)即可,选两个模数合并一下答案。

Code

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define fo(i,j,k) for(int i=j;i<=k;++i)
#define fd(i,j,k) for(int i=j;i>=k;--i)
using namespace std;
typedef long long ll;
const int N=5e4+10,P=1e5+3,mo1=998244353,mo2=1004535809;
ll ny1,ny2;
const ll Mo=1002772198720536577ll;
int a[N],fn,mo;
ll f[18][N<<2],g[18][N<<2],w[N<<2];
ll pow(ll x,int y){
    ll b=1;
    for(;y;y>>=1,x=x*x%mo) if(y&1) b=b*x%mo;
    return b;
}
void NTT(ll *a,int fn,int sig){
    int p=0;
    fo(i,0,fn-1){
        if(i<p) swap(a[i],a[p]);
        for(int j=fn>>1;(p^=j)<j;j>>=1);
    }
    for(int m=2;m<=fn;m<<=1){
        int half=m>>1,t=fn/m;
        fo(i,0,half-1){
            int w0=sig>0?w[t*i]:w[fn-t*i];
            for(int j=i;j<fn;j+=m){
                ll u=a[j],v=a[j+half]*w0%mo;
                a[j]=(u+v)%mo,a[j+half]=(u-v+mo)%mo;
            }
        }
    }
    ll ny=pow(fn,mo-2);
    if(sig<0) fo(i,0,fn-1) a[i]=a[i]*ny%mo;
}
ll mul(ll x,ll y,ll k) {
    x%=k,y%=k;
    ll tmp=(ll)((long double)x*y/k+1e-8)*k;
    return (x*y-tmp+k)%k;
}
void fz(int dep,int l,int r){
    if(l==r){
        f[dep][0]=1,f[dep][1]=a[l];
        g[dep][0]=1,g[dep][1]=a[l];
        return;
    }
    int fn,mid=(l+r)>>1;
    for(fn=1;fn<=r-l+2;fn<<=1);
    fz(dep+1,l,mid);
    fo(i,0,fn-1) g[dep][i]=f[dep][i]=f[dep+1][i],f[dep+1][i]=0;
    fz(dep+1,mid+1,r);
    fo(i,0,fn-1) g[dep+1][i]=f[dep+1][i];
    //mo1
    mo=mo1,w[0]=1,w[1]=pow(3,(mo-1)/fn);
    fo(i,2,fn) w[i]=w[i-1]*w[1]%mo;
    NTT(f[dep],fn,1),NTT(f[dep+1],fn,1);
    fo(i,0,fn-1) f[dep][i]=f[dep][i]*f[dep+1][i]%mo,f[dep+1][i]=0;
    NTT(f[dep],fn,-1);
    //mo2
    mo=mo2,w[0]=1,w[1]=pow(3,(mo-1)/fn);
    fo(i,2,fn) w[i]=w[i-1]*w[1]%mo;
    NTT(g[dep],fn,1),NTT(g[dep+1],fn,1);
    fo(i,0,fn-1) g[dep][i]=g[dep][i]*g[dep+1][i]%mo,g[dep+1][i]=0;
    NTT(g[dep],fn,-1);
    //merge
    fo(i,0,fn-1){
        ll t=(mul(f[dep][i],ny1,Mo)+mul(g[dep][i],ny2,Mo))%Mo%P;
        f[dep][i]=t;
    }
}
int main()
{
    mo=mo1,ny1=pow(mo2,mo-2)*mo2;
    mo=mo2,ny2=pow(mo1,mo-2)*mo1;
    int n,q;
    scanf("%d %d",&n,&q);
    fo(i,1,n) scanf("%d",&a[i]),a[i]%=P;
    fz(1,1,n);
    while(q--){
        int x;
        scanf("%d",&x);
        printf("%lld
",f[1][x]);
    }
}

以上是关于51nod1348乘积之和的主要内容,如果未能解决你的问题,请参考以下文章

51nod 1348 乘积之和

快速数论变换NTT模板

51Nod 1239 欧拉函数之和

51Nod 1244 莫比乌斯函数之和

hdu6153 A Secret CCPC网络赛 51nod 1277 KMP

莫比乌斯函数之和 51Nod - 1244 (杜教筛)