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


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

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

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

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

    那么假设面值(i)(a_i)个,那么最终的答案是

    [G(x)=prod_{i=1}^{1000} (1+{a_ichoose 1}x+{a_ichoose 2}x^2dots) ]

    (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;
    }
    
    

    可还行...

  • 相关阅读:
    java学习之成员内部类
    上海 炉石面试题及解析
    利用RALL机制来事项String类的赋值操作
    关于信号打断正在读取终端的read与select来监视0文件描述符的问题
    ubuntu16.04开机花屏蓝屏解决方案
    gcc 中__thread 关键字的示例代码
    成长、责任和公司的关系
    团队贡献分
    团队如何做决定
    分析app的NABCD
  • 原文地址:https://www.cnblogs.com/winlere/p/11009558.html
Copyright © 2020-2023  润新知