【题解】数字组合(NTT+组合 滑稽)

匿名 (未验证) 提交于 2019-12-02 23:41:02

今天实践一下谢总讲的宰牛刀233,滑稽。

\((1+x)(1+x)(1+x)\)\(x^2\)系数就代表了有三个一快钱硬币构成的两块钱的方案数量。

很好理解,毕竟拆括号这种东西本身就有组合意义。

那么假设面值\(i\)\(a_i\)个,那么最终的答案是
\[ G(x)=\prod_{i=1}^{1000} (1+{a_i\choose 1}x+{a_i\choose 2}x^2\dots) \]

\(x^m\)项系数
直接NTT即可。

//@winlere #include<iostream> #include<cstdio> #include<cstring> #include<algorithm>  using namespace std;  typedef long long ll; inline int qr(){       register int ret=0,f=0;       register char c=getchar();       while(c<48||c>57)f|=c==45,c=getchar();       while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();       return f?-ret:ret; } int la,lb; const int maxn=1<<17|1; int a[maxn],b[maxn]; const int mod=998244353; namespace poly{               inline int Pow(int base,const int&p){         register int ret=1;         for(register int t=p;t;t>>=1,base=1ll*base*base%mod)           if(t&1) ret=1ll*ret*base%mod;         return ret;       }              const int g=3;       const int gi=Pow(g,mod-2);              int A[maxn],B[maxn],r[maxn];       int savlen=-1;       inline void getr(const int&len){         if(len==savlen)return;         savlen=len;         int cnt=0;         for(register int t=1;t<len;t=t<<1)++cnt;         for(register int t=0;t<len;++t)           r[t]=r[t>>1]>>1|(t&1)<<cnt>>1;       }              inline int getlen(const int&len){         register int ret;         for(ret=1;ret<len;ret<<=1);         return ret;       }              inline void NTT(int*a,const int&len,const int&tag){         getr(len);         for(register int t=0;t<len;++t)           if(r[t]>t)swap(a[t],a[r[t]]);         int *a0,*a1,s;         if(s=g,tag!=1)s=gi;         for(register int t=1,wn;t<len;t<<=1){           wn=Pow(s,(mod-1)/(t<<1));           for(register int i=0;i<len;i+=t<<1){             a1=(a0=a+i)+t;             for(register int k=0,w=1,temp;k<t;++k,++a0,++a1,w=1ll*w*wn%mod){                   temp=1ll**a1*w%mod;                   *a1=(*a0-temp)%mod;                   *a0=(*a0+temp)%mod;                   if(*a1<0)*a1+=mod;             }           }         }         if(tag!=1) for(register int t=0,inv=Pow(len,mod-2);t<len;++t)                  a[t]=1ll*a[t]*inv%mod;       }              void inv(int*a,int*b,const int&len){         if(len==1){b[0]=Pow(a[0],mod-2);return;}         inv(a,b,len>>1);         for(register int t=0;t<len;++t) A[t]=a[t],B[t]=b[t];         NTT(A,len<<1,1);NTT(B,len<<1,1);         for(register int t=0,ed=len<<1;t<ed;++t) A[t]=1ll*A[t]*B[t]%mod*1ll*B[t]%mod;         NTT(A,len<<1,-1);         for(register int t=0;t<len;++t) b[t]=((b[t]+b[t])%mod+mod-A[t])%mod;       }              void INV(int*a,int*b,const int&len){         inv(a,b,getlen(len));       }              inline void MLP(int*a,int*b,int*c,const int&len1,const int&len2){         int k=getlen(len1+len2+2);         NTT(a,k,1);NTT(b,k,1);         for(register int t=0;t<=k;++t) c[t]=1ll*a[t]*b[t]%mod;         NTT(c,k,-1);       } }  ll jc[501],v[501]; int buk[1001];  using namespace poly; inline ll c(const int&n,const int&m){       if(n<m)return 0;       return jc[n]*v[m]%mod*v[n-m]%mod; }  int main(){       jc[0]=v[0]=1;       for(register int t=1;t<=500;++t){         jc[t]=jc[t-1]*t%mod;         v[t]=Pow(jc[t],mod-2);       }       int n=qr(),m=qr();       a[0]=1;       for(register int t=1;t<=n;++t) ++buk[qr()];       for(register int t=1;t<=1000;++t){         if(!buk[t])continue;         if(t>m)break;         memset(b,0,sizeof b);         b[0]=1;         for(register int i=1;1ll*t*i<=m&&i<=buk[t];++i)           b[i*t]=c(buk[t],i),lb=i*t;         MLP(a,b,a,la,lb);         la+=lb;         if(la>m+1){           for(register int t=m+1;t<=la;++t)             a[t]=0;           la=m;         }       }       cout<<a[m]<<endl;       return 0; } 

可还行...

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!