码迷,mamicode.com
首页 > 其他好文 > 详细

【51nod1348】乘积之和

时间:2018-06-28 16:00:11      阅读:166      评论:0      收藏:0      [点我收藏+]

标签:区间   ring   pow   type   直接   span   组成   can   ret   

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}(0\leq x\leq r-l+1)\)表示区间\([l,r]\)选了\(x\)个的乘积和,可以发现\(f_{l,r}\)\(f_{l,p}\)\(f_{p+1,r}(l\leq 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\n",f[1][x]);
    }
}

【51nod1348】乘积之和

标签:区间   ring   pow   type   直接   span   组成   can   ret   

原文地址:https://www.cnblogs.com/sadstone/p/9238915.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!