bzoj3625:[Codeforces Round #250]小朋友和二叉树

bzoj传送门

luogu

生成函数,多项式

首先考虑这个题最显然的\(dp\)方程,设\(f(n)\)为根节点权值为\(n\)的二叉树个数,\(g(n)\)为权值为\(n\)的点是否存在

\(n=0\)\(f(n)=1\)

\(n\neq 0\)
\[ f(n)=\sum_{i=1}^{n}g(n)\sum_{j=1}^{n-i}f(j)f(n-j-i) \]
然后设生成函数
\[ F(x)=\sum_{i=0}^{+\infty}f(n)x^i\\ G(x)=\sum_{i=0}^{+\infty}g(n)x^i\\ \]
可以得到
\[ F(x)=F^2(x)G(x)+1 \]
最后得到
\[ F(x)=\frac{2}{1+\sqrt{1-4G(x)}} \]
多项式开根+多项式求逆就好了

bzoj太卡了,没卡过,但是CF上AC了

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
void read(int &x) {
    char ch; bool ok;
    for(ok=0,ch=getchar(); !isdigit(ch); ch=getchar()) if(ch=='-') ok=1;
    for(x=0; isdigit(ch); x=x*10+ch-'0',ch=getchar()); if(ok) x=-x;
}
#define rg register
const int maxn=8e5+10,mod=998244353,g=3,gi=332748118;
char ch[maxn];
int n,m,tot,k,now=1,r[maxn],t[maxn],a[maxn],b[maxn],w[maxn],h[maxn],d[maxn],c[maxn],f[maxn],s[maxn];
int mul(int x,int y){return 1ll*x*y-1ll*x*y/mod*mod;}
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int del(int x,int y){return x-y<0?x-y+mod:x-y;}
int mi(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=mul(ans,a);
        b>>=1,a=mul(a,a);
    }
    return ans;
}
void ntt(int *a,int n,int f){
    for(rg int i=0;i<n;i++)if(r[i]>i)swap(a[i],a[r[i]]);
    for(rg int i=1;i<n;i<<=1){
        int wn=mi(f?g:gi,(mod-1)/(i<<1));
        for(rg int j=0;j<n;j+=i<<1){
            int w=1;
            for(rg int k=0;k<i;k++){
                int x=a[j+k],y=mul(w,a[j+k+i]);
                a[j+k]=add(x,y),a[j+k+i]=del(x,y),w=mul(w,wn);
            }
        }
    }
    if(f)return ;int inv=mi(n,mod-2);
    for(rg int i=0;i<n;i++)a[i]=mul(a[i],inv);
}
void get_inv(int *a,int *b,int n){
    if(n==1)return b[0]=mi(a[0],mod-2),void(); 
    get_inv(a,b,(n+1)>>1);int m,len=0;for(m=1;m<=n<<1;m<<=1)len++;
    for(rg int i=0;i<m;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
    for(rg int i=0;i<n;i++)c[i]=a[i];
    for(rg int i=n;i<m;i++)c[i]=0;
    ntt(c,m,1),ntt(b,m,1);
    for(rg int i=0;i<m;i++)b[i]=del(mul(2,b[i]),mul(c[i],mul(b[i],b[i])));
    ntt(b,m,0);for(rg int i=n;i<m;i++)b[i]=0;
}
void get_ln(int *a,int *b,int n){
    for(rg int i=0;i<n;i++)w[i]=mul(a[i+1],i+1);
    get_inv(a,h,n);int m,len=0;for(m=1;m<=n<<1;m<<=1)len++;
    for(rg int i=0;i<m;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
    ntt(h,m,1),ntt(w,m,1);
    for(rg int i=0;i<m;i++)w[i]=mul(w[i],h[i]);
    ntt(w,m,0);b[0]=0;
    for(rg int i=0;i<m;i++)b[i+1]=mul(w[i],mi(i+1,mod-2)),h[i]=w[i]=0;
    for(rg int i=n;i<m;i++)b[i]=0; 
}
void get_exp(int *a,int *b,int n){
    if(n==1)return b[0]=1,void();
    get_exp(a,b,(n+1)>>1),get_ln(b,s,n);
    int m,len=0;for(m=1;m<=n<<1;m<<=1)len++;
    for(rg int i=0;i<m;i++)r[i]=(r[i>>1]>>1)|((i&1)<<(len-1));
    for(rg int i=0;i<n;i++)s[i]=del(a[i],s[i]);s[0]=add(s[0],1);
    ntt(s,m,1),ntt(b,m,1);
    for(rg int i=0;i<m;i++)b[i]=mul(b[i],s[i]);
    ntt(b,m,0);
    for(rg int i=n;i<m;i++)b[i]=0;
}
int main()
{
    read(n),read(k);
    for(rg int i=1,x;i<=n;i++)read(x),t[x]=1;n=k+1;k=mi(2,mod-2);
    for(rg int i=0;i<=n;i++)t[i]=mul(t[i],4),t[i]=del(0,t[i]);
    t[0]=add(t[0],1);get_ln(t,d,n);
    for(rg int i=0;i<n;i++)d[i]=mul(d[i],k);
    get_exp(d,f,n);f[0]=add(f[0],1);
    for(rg int i=0;i<n;i++)d[i]=0;
    get_inv(f,d,n);
    for(rg int i=0;i<n;i++)d[i]=mul(d[i],2);
    for(rg int i=1;i<n;i++)printf("%d\n",d[i]);
}

转载于:https://www.cnblogs.com/lcxer/p/10858730.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值