Luogu5162 WD与积木(生成函数+多项式求逆)

  显然的做法是求出斯特林数,但没有什么优化空间。

  考虑一种暴力dp,即设f[i]为i块积木的所有方案层数之和,g[i]为i块积木的方案数。转移时枚举第一层是哪些积木,于是有f[i]=g[i]+ΣC(i,j)·f[i-j],g[i]=ΣC(i,j)·g[i-j] (j=1~i)。

  考虑优化 。我们发现这个转移非常像卷积。写成卷积形式,有f[i]=g[i]+Σi!·Σf[i-j]/j!/(i-j)!,g[i]=i!·Σg[i-j]/j!/(i-j)!。直接分治NTT即可。

  诶是不是强行多了个log?考虑构造出生成函数。g比较简单,将该式写成g[i]/i!=Σg[i-j]/j!/(i-j)!,设g[i]/i!生成函数为G(x),inv(i!)生成函数为H(x),则有G(x)=G(x)·H(x)-G(x)+1,也即G(x)=1/(2-H(x))。f同样写成f[i]/i!=g[i]/i!+Σf[i-j]/j!/(i-j)!,则有F(x)=G(x)+F(x)·H(x)-F(x)-1,也即F(x)=G(x)·(G(x)-1)。多项式求逆即可。

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define P 998244353
#define N 100010
char getc(){char c=getchar();while ((c<‘A‘||c>‘Z‘)&&(c<‘a‘||c>‘z‘)&&(c<‘0‘||c>‘9‘)) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<‘0‘||c>‘9‘) {if (c==‘-‘) f=-1;c=getchar();}
    while (c>=‘0‘&&c<=‘9‘) x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int T,n,t,r[1<<19],f[1<<19],g[1<<19],h[1<<19],tmp[1<<19];
int ksm(int a,int k)
{
    int s=1;
    for (;k;k>>=1,a=1ll*a*a%P) if (k&1) s=1ll*s*a%P;
    return s;
}
int inv(int a){return ksm(a,P-2);}
void DFT(int *a,int n,int g)
{
    for (int i=0;i<n;i++) if (i<r[i]) swap(a[i],a[r[i]]);
    for (int i=2;i<=n;i<<=1)
    {
        int wn=ksm(g,(P-1)/i);
        for (int j=0;j<n;j+=i)
        {
            int w=1;
            for (int k=j;k<j+(i>>1);k++,w=1ll*w*wn%P)
            {
                int x=a[k],y=1ll*w*a[k+(i>>1)]%P;
                a[k]=(x+y)%P,a[k+(i>>1)]=(x-y+P)%P;
            }
        }
    }
}
void mul(int *a,int *b,int n,int op)
{
    for (int i=0;i<n;i++) r[i]=(r[i>>1]>>1)|(i&1)*(n>>1);
    DFT(a,n,3),DFT(b,n,3);
    if (op==0) for (int i=0;i<n;i++) a[i]=1ll*a[i]*b[i]%P;
    else for (int i=0;i<n;i++) a[i]=1ll*a[i]*(P+2-1ll*a[i]*b[i]%P)%P;
    DFT(a,n,inv(3));
    int t=inv(n);
    for (int i=0;i<n;i++) a[i]=1ll*a[i]*t%P;
}
void getinv(int n)
{
    g[0]=1;
    for (int i=2;i<=n;i<<=1)
    {
        for (int j=i;j<(i<<1);j++) tmp[j]=0;
        for (int j=0;j<i;j++) tmp[j]=h[j];
        mul(g,tmp,i<<1,1);
        for (int j=i;j<(i<<1);j++) g[j]=0;
    }
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("a.in","r",stdin);
    freopen("a.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    T=read();int t=1;while (t<=(N<<1)) t<<=1;
    h[0]=h[1]=1;for (int i=2;i<=N;i++) h[i]=P-1ll*(P/i)*h[P%i]%P;
    for (int i=2;i<=N;i++) h[i]=1ll*h[i]*h[i-1]%P;
    for (int i=0;i<=N;i++) h[i]=(P-h[i])%P;
    h[0]=(h[0]+2)%P;
    getinv(t);
    for (int i=N+1;i<t;i++) g[i]=0;
    memcpy(f,g,sizeof(f));f[0]--;if (f[0]<0) f[0]+=P;
    mul(f,g,t,0);DFT(g,t,inv(3));int u=inv(t);for (int i=0;i<t;i++) g[i]=1ll*g[i]*u%P;
    while (T--)
    {
        n=read();
        printf("%d\n",1ll*f[n]*inv(g[n])%P);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Gloid/p/10203890.html

时间: 2024-07-31 12:41:19

Luogu5162 WD与积木(生成函数+多项式求逆)的相关文章

[BZOJ3456]城市规划(生成函数+多项式求逆+多项式求ln)

城市规划 时间限制:40s      空间限制:256MB 题目描述 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了.  刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一样, 那么这两个方案就是不同的, 否则就是相同的. 现在你需要求出一共有多少不同的方案.  好了, 这就

[Luogu5162]WD与积木(多项式求逆)

不要以为用上Stirling数就一定离正解更近,FFT都是从DP式本身出发的. 设f[i]为i个积木的所有方案的层数总和,g[i]为i个积木的方案数,则答案为$\frac{f[i]}{g[i]}$ 转移枚举第一层是哪些积木:$$f_n=g_n+\sum_{i=1}^{n}\binom{n}{i}f_{n-i},f_0=0$$$$g_n=\sum_{i=1}^{n}\binom{n}{i}g_{n-i},g_0=1$$ 转化成卷积形式:$$\frac{f_n}{n!}=\frac{g_n}{n!}

CF438E The Child and Binary Tree(生成函数+多项式开根+多项式求逆)

传送门 可以……这很多项式开根模板……而且也完全不知道大佬们怎么把这题的式子推出来的…… 首先,这题需要多项式开根和多项式求逆.多项式求逆看这里->这里,这里讲一讲多项式开根 多项式开方:已知多项式$A$,求多项式$B$满足$A^2\equiv B\pmod{x^n}$(和多项式求逆一样这里需要取模,否则$A$可能会有无数项) 假设我们已经求出$A'^2\equiv B\pmod{x^n}$,考虑如何计算出$A^2\equiv B\pmod{x^{2n}}$ 首先肯定存在$A^2\equiv B

多项式求逆 学习总结

感觉蒟蒻现在学多项式求逆貌似有些晚了 不过真的很有意思了(然而省选的时候自己还在玩泥巴什么也不会 多项式求逆是基于倍增的 假设我们知道 h(x)f(x)=1(mod x^n) 移项得 (h(x)*f(x)-1)=0(mod x^n) 两边同时求平方得 h(x)^2*f(x)^2 - 2*h(x)*f(x) +1 = 0 (mod x^2n) 设g(x)*f(x)=1(mod x^2n) 两边同时乘以g(x)可以得 h(x)^2*f(x) -2*h(x) + g(x) =0 (mod x^2n)

Nescafe41 ProblemA 异化多肽 多项式求逆

题目大意:. 思路: 搞出C的生成函数F(x),那么: 长度为1的答案为F(x) 长度为2的答案为F2(x) - 故最终的答案为 F(x)+F2(x)+F3(x)+... =1?F+∞(x)1?F(x) =11?F(x) 然后就是多项式求逆了= = 跪picks大毒瘤 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M (263000&

【BZOJ】4555: [Tjoi2016&amp;Heoi2016]求和 排列组合+多项式求逆 或 斯特林数+NTT

[题意]给定n,求Σi=0~nΣj=1~i s(i,j)*2^j*j!,n<=10^5. [算法]生成函数+排列组合+多项式求逆 [题解]参考: [BZOJ4555][Tjoi2016&Heoi2016]求和-NTT-多项式求逆 $ans=\sum_{i=0}^{n}\sum_{j=0}^{i}s(i,j)*2^j*j!$ 令$g(n)=\sum_{j=0}^{n}s(n,j)*2^j*j!$ 则ans是Σg(i),只要计算出g(i)的生成函数就可以统计答案. g(n)可以理解为将n个数划分

[Codeforces438E][bzoj3625] 小朋友和二叉树 [多项式求逆+多项式开根]

题面 传送门 思路 首先,我们把这个输入的点的生成函数搞出来: \(C=\sum_{i=0}^{lim}s_ix^i\) 其中\(lim\)为集合里面出现过的最大的数,\(s_i\)表示大小为\(i\)的数是否出现过 我们再设另外一个函数\(F\),定义\(F_k\)表示总权值为\(k\)的二叉树个数 那么,一个二叉树显然可以通过两个子树(可以权值为0,也就是空子树)和一个节点构成 那么有如下求\(F\)的式子 \(F_0=1\) \(F_k=\sum_{i=0}^k s_i \sum_{j=0

NTT+多项式求逆+多项式开方(BZOJ3625)

定义多项式h(x)的每一项系数hi,为i在c[1]~c[n]中的出现次数. 定义多项式f(x)的每一项系数fi,为权值为i的方案数. 通过简单的分析我们可以发现:f(x)=2/(sqrt(1-4h(x))+1) 于是我们需要多项式开方和多项式求逆. 多项式求逆: 求B(x),使得A(x)*B(x)=1 (mod x^m) 考虑倍增. 假设我们已知A(x)*B(x)=1 (mod x^m),要求C(x),使得A(x)*C(x)=1 (mod x^(2m)) 简单分析可得C(x)=B(x)*(2-A

【BZOJ 3456】 3456: 城市规划 (NTT+多项式求逆)

3456: 城市规划 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 658  Solved: 364 Description 刚刚解决完电力网络的问题, 阿狸又被领导的任务给难住了. 刚才说过, 阿狸的国家有n个城市, 现在国家需要在某些城市对之间建立一些贸易路线, 使得整个国家的任意两个城市都直接或间接的连通. 为了省钱, 每两个城市之间最多只能有一条直接的贸易路径. 对于两个建立路线的方案, 如果存在一个城市对, 在两个方案中是否建立路线不一