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\pmod{x^n}$

然后两式相减$$A‘^2-A^2\equiv 0\pmod{x^n}$$

$$(A‘-A)(A‘+A)\equiv 0\pmod{x^n}$$

我们假设$A‘-A\equiv 0\pmod{x^n}$,然后两边平方$$A‘^2-2A‘A+A^2\equiv 0\pmod{x^{2n}}$$

(关于平方之后模数变化的原因可以看我多项式求逆那篇文章,里面有写)

又因为$A^2\equiv B\pmod{x^{2n}}$,代入得$$A‘^2-2A‘A+B\equiv 0\pmod{x^{2n}}$$

$$A\equiv\frac{A‘^2-B}{2A‘}\pmod{x^{2n}}$$

那么这个只要递归计算就可以了

然后多项式开方就讲到这里

下面说一下本题的做法

首先,我也不知道怎么想到的构造出生成函数,$C=\sum_{i=1}^{lim}s_ix^i$,其中$s_i$表示$i$是否在集合中出现过,然后再设一个$F_k$表示权值为$k$时的答案

因为一棵二叉树可以由根节点,左右子树构成(左右子树可以是空的)

那么存在如下关系$$F_k=1+\sum_{i=1}^ks_i\sum_{j=0}^{k-i}F_jF_{i-j-k}$$

然后我也不知道怎么看出来的发现这是一个卷积的形式,即$$F=1+C*F*F$$(这里$*$是多项式乘法)

把它看做一个一元二次方程求解,得$$F=\frac{1\pm \sqrt{1-4C}}{2C}=\frac{2}{1\pm\sqrt{1-4C}}$$

然后因为$F_0=1,C_0=0$,所以符号取正,即$$F=\frac{2}{1+\sqrt{1-4C}}$$

那么把多项式开根和多项式求逆的板子带进去就好了

 1 //minamoto
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<algorithm>
 5 #define swap(x,y) (x^=y,y^=x,x^=y)
 6 #define mul(x,y) (1ll*x*y%P)
 7 #define add(x,y) (x+y>=P?x+y-P:x+y)
 8 #define dec(x,y) (x-y<0?x-y+P:x-y)
 9 using namespace std;
10 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
11 char buf[1<<21],*p1=buf,*p2=buf;
12 inline int read(){
13     #define num ch-‘0‘
14     char ch;bool flag=0;int res;
15     while(!isdigit(ch=getc()))
16     (ch==‘-‘)&&(flag=true);
17     for(res=num;isdigit(ch=getc());res=res*10+num);
18     (flag)&&(res=-res);
19     #undef num
20     return res;
21 }
22 char sr[1<<21],z[20];int K=-1,Z;
23 inline void Ot(){fwrite(sr,1,K+1,stdout),K=-1;}
24 inline void print(int x){
25     if(K>1<<20)Ot();if(x<0)sr[++K]=45,x=-x;
26     while(z[++Z]=x%10+48,x/=10);
27     while(sr[++K]=z[Z],--Z);sr[++K]=‘\n‘;
28 }
29 const int N=500005,P=998244353,G=3,inv2=499122177;
30 inline int ksm(int a,int b){
31     int res=1;
32     while(b){
33         if(b&1) res=mul(res,a);
34         a=mul(a,a),b>>=1;
35     }
36     return res;
37 }
38 int n,m,r[N],A[N],B[N],C[N],D[N],O[N],d[N],c[N];
39 void NTT(int *A,int type,int len){
40     int limit=1,l=0;
41     while(limit<len) limit<<=1,++l;
42     for(int i=0;i<limit;++i)
43     r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
44     for(int i=0;i<limit;++i)
45     if(i<r[i]) swap(A[i],A[r[i]]);
46     for(int mid=1;mid<limit;mid<<=1){
47         int R=mid<<1,Wn=ksm(G,(P-1)/R);O[0]=1;
48         for(int j=1;j<mid;++j) O[j]=mul(O[j-1],Wn);
49         for(int j=0;j<limit;j+=R){
50             for(int k=0;k<mid;++k){
51                 int x=A[j+k],y=mul(O[k],A[j+k+mid]);
52                 A[j+k]=add(x,y),A[j+k+mid]=dec(x,y);
53             }
54         }
55     }
56     if(type==-1){
57         reverse(A+1,A+limit);
58         for(int i=0,inv=ksm(limit,P-2);i<limit;++i)
59         A[i]=mul(A[i],inv);
60     }
61 }
62 void Inv(int *a,int *b,int len){
63     if(len==1) return (void)(b[0]=ksm(a[0],P-2));
64     Inv(a,b,len>>1);
65     for(int i=0;i<len;++i) A[i]=a[i],B[i]=b[i];
66     NTT(A,1,len<<1),NTT(B,1,len<<1);
67     for(int i=0,l=(len<<1);i<l;++i) A[i]=mul(mul(A[i],B[i]),B[i]);
68     NTT(A,-1,len<<1);
69     for(int i=0;i<len;++i) b[i]=dec(1ll*(b[i]<<1)%P,A[i]);
70     for(int i=0,l=(len<<1);i<l;++i) A[i]=B[i]=0;
71 }
72 void Sqrt(int *a,int *b,int len){
73     if(len==1) return (void)(b[0]=a[0]);
74     Sqrt(a,b,len>>1);
75     for(int i=0;i<len;++i) C[i]=a[i];
76     Inv(b,D,len);
77     NTT(C,1,len<<1),NTT(D,1,len<<1);
78     for(int i=0,l=len<<1;i<l;++i) D[i]=mul(D[i],C[i]);
79     NTT(D,-1,len<<1);
80     for(int i=0;i<len;++i) b[i]=mul(add(b[i],D[i]),inv2);
81     for(int i=0,l=(len<<1);i<l;++i) C[i]=D[i]=0;
82 }
83 int main(){
84 //    freopen("testdata.in","r",stdin);
85     n=read(),m=read();
86     for(int i=1,x;i<=n;++i) x=read(),++d[x];
87     int len;for(len=1;len<=m;len<<=1);
88     for(int i=1;i<len;++i) d[i]=(-4*d[i]+P)%P;
89     ++d[0];
90     Sqrt(d,c,len);
91     for(int i=0;i<len;++i) d[i]=0;
92     c[0]=add(c[0],1);
93     Inv(c,d,len);
94     for(int i=0;i<=m;++i) d[i]=add(d[i],d[i]);
95     for(int i=1;i<=m;++i) print(d[i]);
96     Ot();
97     return 0;
98 }

原文地址:https://www.cnblogs.com/bztMinamoto/p/9747126.html

时间: 2024-08-29 22:27:37

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

CF438E The Child and Binary Tree——生成函数

题面 CF438E 解析 一开始又把题读错了... 设$g_i=1/0$表示数$i$是否在$c$中出现过,$f_i$表示权值和为$i$的二叉树个数,有下式:$$f_i=\sum_{j=1}^{m}g_j\sum_{k=0}^{i-j}f_k f_{i-j-k}$$ 设$F(x)=\sum_{i=0}^{\infty}f_i x^i$, $G(x)=\sum_{i=0}^{\infty}g_i x^i$,有:$$F=G*F^2 + 1$$ 后面$+1$是因为$g_0=0$而$f_0=1$ 求根公式

CF438E The Child and Binary Tree

题目 生成函数就是好,什么题目都能搞 先来列一个暴力\(dp\),\(dp_i\)表示形成\(i\)点权的二叉树的方案数 我们可以直接列出方程 \[ dp_i=\sum_{k=1}^n\sum_{j=0}^{i-c_k}dp_jdp_{i-c_k-j} \] 边界条件\(dp_0=1\) 发现里面类似卷积,于是生成函数来搞 \[ F(x)=\sum_{i=0}([i=0]+\sum_{k=1}^n\sum_{j=0}^{i-c_k}dp_jdp_{i-c_k-j})x^i \] \[ F(x)=

cf438E. The Child and Binary Tree(NTT 多项式开根 多项式求逆)

题意 链接 Sol 生成函数博大精深Orz 我们设\(f(i)\)表示权值为\(i\)的二叉树数量,转移的时候可以枚举一下根节点 \(f(n) = \sum_{w \in C_1 \dots C_n} \sum_{j=0}^{n-w} f(j) f(n-w-j)\) 设\(T =n-w\),后半部分变为\(\sum_{j=0}^T f(j) f(T-j)\),是个标准的卷积形式. 对于第一重循环我们可以设出现过的数的生成函数\(C(x)\) 可以得到\(f = C * f * f + 1\),+

codeforces #250E The Child and Binary Tree 快速傅里叶变换

题目大意:给定一个集合S,对于i=1...m求有多少二叉树满足每个节点的权值都在集合S中且权值和为i 构造答案多项式F(x)和集合S的生成函数C(x),那么 根节点的左子树是一棵二叉树,右子树是一棵二叉树,本身的权值必须在集合S中,此外还有空树的情况 故有F(x)=F2(x)C(x)+1 解得F(x)=1±1?4C(x)√2C(x)=21±1?4C(x)√ 若等式下方取减号则分母不可逆,舍去 得到F(x)=21+1?4C(x)√ 有关多项式求逆和多项式开根的内容参见Picks的博客 CF上每个点

LeetCode Binary Tree Preorder Traversal 先根遍历

题意:给一棵树,求其先根遍历的结果. 思路: (1)深搜法: 1 /** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode(int x) : val(x), left(NULL), right(NULL) {} 8 * }; 9 */ 10 class Solution { 11 public

[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

P5205 【模板】多项式开根

\(\color{#0066ff}{ 题目描述 }\) 给定一个\(n-1\)次多项式\(A(x)\),求一个在\(\bmod\ x^n\)意义下的多项式\(B(x)\),使得\(B^2(x) \equiv A(x) \ (\bmod\ x^n)\) 多项式的系数在\(\bmod\ 998244353\)的意义下进行运算. \(\color{#0066ff}{输入格式}\) 第一行一个正整数\(n\). 接下来\(n\)个整数,依次表示多项式的系数\(a_0, a_1, \dots, a_{n-

bzoj 3625(CF 438E)The Child and Binary Tree——多项式开方

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3625 http://codeforces.com/contest/438/problem/E 开方:https://blog.csdn.net/kscla/article/details/79356786 不过还是不会二次剩余. 也不知道为什么取了 G(x)-B(x)=0 而不是 G(x)+B(x)=0. 式子是  B(x) = ( A(x) + G2(x) ) / 2*G(x) ,但写的

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