[UOJ50]链式反应
Posted jefflyy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[UOJ50]链式反应相关的知识,希望对你有一定的参考价值。
这个题意说人话就是:一棵带标号的有根树,编号满足堆性质,根节点有$x$个儿子是叶子($xin A$),另外的$2$个儿子也是这样的一棵树,求不同的树的个数
设$f_n$为答案,枚举那两棵子树的大小$i,j$,那么$f_i=frac12sumlimits_{1leq j,klt i}[i-1-j-kin A]inom{i-1}jinom{i-1-j}kf_jf_k$,$f_1=1$,直接做是$O(n^3)$的
把组合数拆开,记$a_i=[iin A]frac1{i!},f‘_i=frac{f_i}{i!},g_i=sumlimits_{j+k=i}f‘_jf‘_k$,那么$f‘_i=frac1{2i}sumlimits_{j+k=i-1}a_jg_k$,按照这个直接做,每次更新$g$,时间复杂度为$O(n^2)$
因为$f,g$的递推式都是卷积,所以考虑分治FFT,这种类CDQ分治的分治FFT主要解决形如$f_i=sumlimits_{j+k=i}g_jh_k$的递推式,其中$g,h$中至少一个与$f$有关
如果当前分治到$[l,r]$,先递归进$[l,mid]$,然后算递推式中$jin[l,mid],iin[mid+1,r]$的部分,一次卷积就可以了
需要注意的是如果$g,h$都与$f$有关,那么我们还要处理$j,kin[l,mid],iin[mid+1,r]$的情况,但因为是分治结构,所以这种情况只会在$l=1$时出现,于是只需在$lgt1$时将答案$ imes2$即可
#include<stdio.h> #include<algorithm> #include<string.h> using namespace std; typedef long long ll; const int mod=998244353; int mul(int a,int b){return(ll)a*b%mod;} int ad(int a,int b){return(a+b)%mod;} int de(int a,int b){return(a-b)%mod;} void inc(int&a,int b){(a+=b)%=mod;} int pow(int a,int b){ int s=1; while(b){ if(b&1)s=mul(s,a); a=mul(a,a); b>>=1; } return s; } int rev[524288],N,iN; void pre(int n){ int i,k; for(N=1,k=0;N<n;N<<=1)k++; for(i=0;i<N;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1)); iN=pow(N,mod-2); } void ntt(int*a,int on){ int i,j,k,t,w,wn; for(i=0;i<N;i++){ if(i<rev[i])swap(a[i],a[rev[i]]); } for(i=2;i<=N;i<<=1){ wn=pow(3,on==1?(mod-1)/i:mod-1-(mod-1)/i); for(j=0;j<N;j+=i){ w=1; for(k=0;k<i>>1;k++){ t=mul(a[i/2+j+k],w); a[i/2+j+k]=de(a[j+k],t); a[j+k]=ad(a[j+k],t); w=mul(w,wn); } } } if(on==-1){ for(i=0;i<N;i++)a[i]=mul(a[i],iN); } } int ta[524288],tb[524288]; void conv(){ ntt(ta,1); ntt(tb,1); for(int i=0;i<N;i++)ta[i]=mul(ta[i],tb[i]); ntt(ta,-1); } int fac[200010],rfac[200010]; char s[200010]; int a[200010],f[200010],g[200010],inv[400010]; void solve(int l,int r){ if(l==r)return; int mid=(l+r)>>1,i; solve(l,mid); //f*f->g pre(r-l+mid-l+1); memset(ta,0,N<<2); memset(tb,0,N<<2); for(i=1;i<=r-l;i++)ta[i-1]=f[i]; for(i=l;i<=mid;i++)tb[i-l]=f[i]; conv(); if(l>1){ for(i=mid-l;i<r-l;i++)ta[i]=mul(ta[i],2); } for(i=1;i<=r-mid;i++)inc(g[i+mid],ta[i+mid-l-1]); //a*g->f memset(ta,0,N<<2); memset(tb,0,N<<2); for(i=0;i<r-l;i++)ta[i]=a[i]; for(i=l;i<=mid;i++)tb[i-l]=g[i]; conv(); for(i=1;i<=r-mid;i++)inc(f[i+mid],mul(inv[2*(i+mid)],ta[i+mid-l-1])); solve(mid+1,r); } int main(){ int n,i; scanf("%d%s",&n,s); fac[0]=1; for(i=1;i<=n;i++)fac[i]=mul(fac[i-1],i); rfac[n]=pow(fac[n],mod-2); for(i=n;i>0;i--)rfac[i-1]=mul(rfac[i],i); inv[1]=1; for(i=2;i<=2*n;i++)inv[i]=-mul(mod/i,inv[mod%i]); for(i=0;i<n;i++)a[i]=s[i]==‘1‘?rfac[i]:0; f[1]=1; solve(1,n); for(i=1;i<=n;i++)printf("%d ",ad(mul(f[i],fac[i]),mod)); }
以上是关于[UOJ50]链式反应的主要内容,如果未能解决你的问题,请参考以下文章