UOJ#50UR #3链式反应(分治FFT,动态规划)

Posted cjyyb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UOJ#50UR #3链式反应(分治FFT,动态规划)相关的知识,希望对你有一定的参考价值。

【UOJ#50】【UR #3】链式反应(分治FFT,动态规划)

题面

UOJ

题解

首先把题目意思捋一捋,大概就是有\(n\)个节点的一棵树,父亲的编号大于儿子。
满足一个点的儿子有\(2+c\)个,其中\(c\in A\),且\(c\)个儿子是叶子,另外\(2\)个存在子树,且两种点的链接的边是不同的,求方案数。
那么就考虑一个暴力\(dp\),设\(f[i]\)表示有\(i\)个节点的树的个数。
那么枚举它两个有子树的子树大小,然后把编号给取出来,得到:
\[f[i]=\frac12\sum_j\sum_k i-1\choose ji-1-j\choose kf[j]f[k]\]
其中存在限制,也就是\(i-1-j-k\in A\),要乘\(\frac12\)是因为这两个子树选择的时候存在先后关系,所以同一棵子树可能会被计算两次。
这样子我们就可以做\(O(n^3)\)了。
然后把式子给拆一下:
\[i-1\choose ji-1-j\choose k=\frac(i-1)!j!k!(i-1-j-k)!\]
之和\(i,j,k\)相关的可以直接和\(f[i],f[j],f[k]\)丢到一起,剩下的只和\(i/j+k\)相关。
然后预处理\(g[i]=\sum_j \fracf[j]j!\fracf[i-j](i-j)!\)
那么
\[\frac2f[i](i-1)!=\sum_p=j+kg[p]*[i-1-p\in A]\]
这样子就可以做到\(O(n^2)\)了。
进一步发现,\(g\)\(\frac2f(i-1)!\)都是卷积的形式,所以可以直接分治\(FFT\)求解做到\(O(nlog^2)\)
然而这里有细节、、、乱搞会挂。
注意到分治\(FFT\)的过程是\([l,mid]\rightarrow [mid+1,r]\),而\(g\)数组在求解的时候是\(f\)卷上\(f\),这样子会导致乘的数组的范围是\([0,r-l]\),可能存在一部分值没有被计算过。
而这个值具有对称性,所以我们强制\(j<k\)的时候才贡献\(2f[j]*f[k]\)
也就是如果\(i<l\)的时候,数组里丢进去的是\(2f[i]\),当\(l\le i\le mid\)的时候丢\(f[i]\),否则丢\(0\)进去。
然后两个\(log\)就能过了。
然而这题看起来有更加优秀的做法,但是窝太菜了就不管了。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MOD 998244353
#define MAX 840000
int fpow(int a,int b)int s=1;while(b)if(b&1)s=1ll*s*a%MOD;a=1ll*a*a%MOD;b>>=1;return s;
int r[MAX],W[MAX];
void NTT(int *P,int opt,int len)

    int N,l=0;for(N=1;N<len;N<<=1)++l;
    for(int i=0;i<N;++i)r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
    for(int i=0;i<N;++i)if(i<r[i])swap(P[i],P[r[i]]);
    for(int i=1;i<N;i<<=1)
    
        int w=fpow(3,(MOD-1)/(i<<1));W[0]=1;
        for(int k=1;k<i;++k)W[k]=1ll*W[k-1]*w%MOD;
        for(int j=0,p=i<<1;j<N;j+=p)
            for(int k=0;k<i;++k)
            
                int X=P[j+k],Y=1ll*W[k]*P[i+j+k]%MOD;
                P[j+k]=(X+Y)%MOD;P[i+j+k]=(X+MOD-Y)%MOD;
            
    
    if(opt==-1)
    
        reverse(&P[1],&P[N]);
        for(int i=0,inv=fpow(N,MOD-2);i<N;++i)P[i]=1ll*P[i]*inv%MOD;
    

int jc[MAX],inv[MAX],jv[MAX];
int f[MAX],g[MAX],A[MAX];
int L[MAX],R[MAX];
void CDQ(int l,int r)

    if(l==r)
    
        if(l==1)f[l]=1;
        else f[l]=1ll*f[l]*inv[2]%MOD*jc[l-1]%MOD;
        return;
    
    int mid=(l+r)>>1,N;
    CDQ(l,mid);
    for(N=1;N<=mid-l+1+r-l;N<<=1);
    for(int i=l;i<=mid;++i)L[i-l]=g[i];
    for(int i=0;i<=r-l;++i)R[i]=A[i]*jv[i];
    NTT(L,1,N);NTT(R,1,N);
    for(int i=0;i<N;++i)L[i]=1ll*L[i]*R[i]%MOD;
    NTT(L,-1,N);
    for(int i=mid+1;i<=r;++i)f[i]=(f[i]+L[i-1-l])%MOD;
    for(int i=0;i<N;++i)L[i]=R[i]=0;
    for(int i=l;i<=mid;++i)L[i-l]=1ll*f[i]*jv[i]%MOD;
    for(int i=0;i<=r-l;++i)
        if(i<l)R[i]=2ll*f[i]*jv[i]%MOD;
        else if(i<=mid)R[i]=1ll*f[i]*jv[i]%MOD;
    NTT(L,1,N);NTT(R,1,N);
    for(int i=0;i<N;++i)L[i]=1ll*L[i]*R[i]%MOD;
    NTT(L,-1,N);
    for(int i=mid+1;i<=r;++i)g[i]=(g[i]+L[i-l])%MOD;
    for(int i=0;i<N;++i)L[i]=R[i]=0;
    
    CDQ(mid+1,r);

int n;
char ch[MAX];
int main()

    scanf("%d%s",&n,ch);
    for(int i=0;i<n;++i)A[i]=ch[i]-48;
    jc[0]=jv[0]=inv[0]=inv[1]=1;
    for(int i=2;i<=n;++i)inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
    for(int i=1;i<=n;++i)jc[i]=1ll*jc[i-1]*i%MOD;
    for(int i=1;i<=n;++i)jv[i]=1ll*jv[i-1]*inv[i]%MOD;
    f[1]=1;
    /*
    for(int i=2;i<=n;++i)
    
        for(int j=0;j<=i;++j)g[i]=(g[i]+1ll*f[j]*jv[j]%MOD*f[i-j]%MOD*jv[i-j]%MOD)%MOD;
        for(int j=0;j<i;++j)
            if(A[i-1-j])
                f[i]=(f[i]+1ll*g[j]*jv[i-1-j])%MOD;
        f[i]=1ll*f[i]*inv[2]%MOD*jc[i-1]%MOD;
    
    */
    CDQ(1,n);
    for(int i=1;i<=n;++i)printf("%d\n",f[i]);
    return 0;

以上是关于UOJ#50UR #3链式反应(分治FFT,动态规划)的主要内容,如果未能解决你的问题,请参考以下文章

UOJ#22UR #1外星人(动态规划)

UOJ#31 UR #2猪猪侠再战括号序列

UOJ#21UR #1缩进优化

[UOJ50]链式反应

UOJ#49. UR #3铀仓库

UOJ#21UR #1缩进优化