[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}^{k-i} F_j F_{k-i-j}\)

显然这个式子是一个卷积的形式

我们把这个三个东西的卷积放到整个函数上来考虑(也就是把\(F_kC_k\)看做多项式的系数做卷积),可以得到:

\(F=C\ast F\ast F +1\),这里的\(\ast\)是多项式乘法

由于多项式乘法和普通乘法一样满足各种定律之类的,所以我们可以解个一元二次方程

\(C\ast F^2 - F + 1=0\)

\(F=\frac{1\pm \sqrt{1-4C}}{2C}\)

先考虑上面是+的情况

此时\({\lim_{x \to 0}}F(x)=+\infty\),舍去

再考虑-的情况

此时\({\lim_{x \to 0}}F(x)=1\),OK

其实到这里就可以算了,一个多项式开方+多项式求逆就解决了

但是我们更进一步,再划一划这个式子,可以变成如下形式:

\(F=\frac{2}{1+\sqrt{1-4C}}\)

多项式开方+多项式求逆解决问题

Code:

这题超!级!卡!常!我这种人傻常数大的,只能参照爷稳稳的代码写一写了qwq

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cassert>
#include<cmath>
#include<ctime>
#define ll long long
#define MOD 998244353
using namespace std;
inline int read(){
    int re=0,flag=1;char ch=getchar();
    while(ch>'9'||ch<'0'){
        if(ch=='-') flag=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    return re*flag;
}
int n,m,K;
ll A[600010],B[600010],C[600010],x[600010],y[600010];
int g=3,ginv,inv2,r[600010],lim,cnt;
ll qpow(ll a,ll b){
    ll re=1;
    while(b){
        if(b&1) re=re*a%MOD;
        a=a*a%MOD;b>>=1;
    }
    return re;
}
ll add(ll a,ll b){
    a+=b;
    return (a>=MOD)?a-MOD:a;
}
ll dec(ll a,ll b){
    a-=b;
    return (a<0)?a+MOD:a;
}
void ntt(ll *a,ll type){
    int i,j,k;ll x,y,w,wn,inv,mid;
    for(i=0;i<lim;i++) if(i<r[i]) swap(a[i],a[r[i]]);
    for(mid=1;mid<lim;mid<<=1){
        wn=qpow((type==1)?g:ginv,(MOD-1)/(mid<<1));
        for(j=0;j<lim;j+=(mid<<1)){
            w=1;
            for(k=0;k<mid;k++,w=w*wn%MOD){
                x=a[j+k];y=a[j+k+mid]*w%MOD;
                a[j+k]=add(x,y);
                a[j+k+mid]=dec(x,y);
            }
        }
    }
    if(~type) return;
    inv=qpow(lim,MOD-2);
    for(i=0;i<lim;i++) a[i]=a[i]*inv%MOD;
}
void getinv(ll *a,ll *b,int len,int lena){
    if(len==1){b[0]=qpow(a[0],MOD-2);return;}
    assert(len==((len>>1)<<1));
    int i,mid=len>>1;
    getinv(a,b,mid,lena);
    lim=1;cnt=0;r[0]=0;
    while(lim<=(len<<1)) lim<<=1,cnt++;
    for(i=0;i<lim;i++) r[i]=((r[i>>1]>>1)|((i&1)<<(cnt-1)));

    for(i=0;i<len;i++) x[i]=a[i];
    for(i=len;i<lim;i++) x[i]=0;
    for(i=0;i<mid;i++) y[i]=b[i];
    for(i=len;i<lim;i++) y[i]=0;

    ntt(x,1);ntt(y,1);
    for(i=0;i<lim;i++) x[i]=y[i]*(2ll-y[i]*x[i]%MOD+MOD)%MOD;
    ntt(x,-1);
    for(i=0;i<len;i++) b[i]=x[i];
}
void getrt(ll *a,ll *b,int len){
    if(len==1){b[0]=1;return;}
    int i,mid=len>>1;

    getrt(a,b,mid);
    memset(C,0,sizeof(C));getinv(b,C,len,len);

    lim=1;cnt=0;r[0]=0;
    while(lim<=(len<<1)) lim<<=1,cnt++;
    for(i=0;i<lim;i++) r[i]=((r[i>>1]>>1)|((i&1)<<(cnt-1))),x[i]=a[i];
    for(i=len;i<lim;i++) x[i]=C[i]=b[i]=0;

    ntt(x,1);ntt(C,1);
    for(i=0;i<lim;i++) x[i]=x[i]*C[i]%MOD;
    ntt(x,-1);

    for(i=0;i<len;i++) b[i]=add(b[i],x[i])*inv2%MOD;
}
void init(){
    ginv=qpow(g,MOD-2);
    inv2=qpow(2,MOD-2);
}
int main(){
    init();
    n=read();m=read();int i,t1;A[0]=K=1;
    for(i=1;i<=n;i++) t1=read(),A[t1]=dec(A[t1],4);
    while(K<=m) K<<=1;

    getrt(A,B,K);
    B[0]=add(B[0],1);
    memset(C,0,sizeof(C));
    getinv(B,C,K,K);
    for(i=1;i<=m;i++) printf("%lld\n",(2*C[i]+MOD)%MOD);
}

原文地址:https://www.cnblogs.com/dedicatus545/p/9366175.html

时间: 2024-07-29 21:31:58

[Codeforces438E][bzoj3625] 小朋友和二叉树 [多项式求逆+多项式开根]的相关文章

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

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

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

luoguP4512 【模板】多项式除法 NTT+多项式求逆+多项式除法

Code: #include<bits/stdc++.h> #define maxn 300000 #define ll long long #define MOD 998244353 #define setIO(s) freopen(s".in","r",stdin) ,freopen(s".out","w",stdout) using namespace std; namespace poly{ #define

BZOJ 3625 多项式求逆+多项式开根

思路: RT //By SiriusRen #include <bits/stdc++.h> using namespace std; const int N=1<<18,mod=998244353; int A[N],C[N],invC[N],c[N],d[N],R[N],tmp[N],xx,len,sqrA[N],F[N]; typedef long long ll; int power(ll x,int y){ ll res=1; while(y){ if(y&1)r

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

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

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

多项式求逆 学习总结

感觉蒟蒻现在学多项式求逆貌似有些晚了 不过真的很有意思了(然而省选的时候自己还在玩泥巴什么也不会 多项式求逆是基于倍增的 假设我们知道 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个数划分