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) ,但写的时候这样比较方便:

令 D(x) = b-1(x) , 则 B(x) =( a(x) * D(x) + b(x) ) / 2;

可以开一个 C(x) 表示 a(x) ,这样就不用动 a 数组了;因为加法和乘法都可以在系数上做,所以也可以不用把 b 数组 ntt 了。

getinv 的时候可以用 A 和 B 表示 a 和 b ,这样就不用把 a 数组和 b 数组 ntt 了。

  F(x) = D(x) * F2(x) + 1 ;其中+1是因为D没有常数项,所以不+1的话F也没有常数项,但 f [0]=0。

  F(x) = ( 1+ sqrt(1-4*D(x)) ) / 2*D(x) ,这里要取减号,因为D没有常数项,如果分子剩下了常数项,就会除出余数,不太对。

  写成  F(x) = 2 / 2*D(x)*sqrt( 1+4*D(x) )  比较方便。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int N=1e5+5,M=N<<2,mod=998244353;
int a[M],b[M],A[M],B[M],C[M],D[M],len,r[M],inv2;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)fx=0;ch=getchar();}
  while(ch>=‘0‘&&ch<=‘9‘) ret=ret*10+ch-‘0‘,ch=getchar();
  return fx?ret:-ret;
}
void upd(int &x){x>=mod?x-=mod:0;}
int pw(int x,int k)
{int ret=1;while(k){if(k&1)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1;}return ret;}
void ntt(int *a,bool fx)
{
  for(int i=0;i<len;i++)
    if(i<r[i])swap(a[i],a[r[i]]);
  for(int R=2;R<=len;R<<=1)
    {
      int Wn=pw( 3,(mod-1)/R );
      if(fx) Wn=pw( Wn,mod-2 );
      for(int i=0,m=R>>1;i<len;i+=R)
    for(int j=0,w=1;j<m;j++,w=(ll)w*Wn%mod)
      {
        int x=a[i+j], y=(ll)w*a[i+m+j]%mod;
        a[i+j]=x+y;  upd(a[i+j]);
        a[i+m+j]=x+mod-y;  upd(a[i+m+j]);
      }
    }
  if(!fx)return; int inv=pw(len,mod-2);
  for(int i=0;i<len;i++)a[i]=(ll)a[i]*inv%mod;
}
void getinv(int n,int *a,int *b)
{
  if(n==1){b[0]=pw(a[0],mod-2);return;}
  getinv(n>>1,a,b);
  len=n<<1;
  for(int i=0;i<len;i++)r[i]=(r[i>>1]>>1)+((i&1)?len>>1:0);
  for(int i=0;i<n;i++)A[i]=a[i],B[i]=b[i];
  ntt(A,0); ntt(B,0);
  for(int i=0;i<len;i++)A[i]=(ll)A[i]*B[i]%mod*B[i]%mod;
  ntt(A,1);
  for(int i=0;i<n;i++)b[i]=b[i]<<1,upd(b[i]);
  for(int i=0;i<n;i++)b[i]=b[i]+mod-A[i],upd(b[i]);
  for(int i=0;i<len;i++)A[i]=0,B[i]=0;//i=0!!! or can‘t clear after a total getinv
}
void getsqr(int n,int *a,int *b)
{
  if(n==1){b[0]=1;return;}
  getsqr(n>>1,a,b);
  for(int i=0;i<n;i++)C[i]=a[i];
  getinv(n,b,D);
  len=n<<1;
  for(int i=0;i<len;i++)r[i]=(r[i>>1]>>1)+((i&1)?len>>1:0);
  ntt(C,0); ntt(D,0);
  for(int i=0;i<len;i++) D[i]=(ll)C[i]*D[i]%mod;
  ntt(D,1);
  for(int i=0;i<n;i++)b[i]=(ll)(D[i]+b[i])*inv2%mod;
  for(int i=0;i<len;i++) C[i]=D[i]=0;//
}
int main()
{
  int n,m; n=rdn(); m=rdn(); inv2=pw(2,mod-2);
  for(int i=1;i<=n;i++)a[rdn()]=mod-4;
  a[0]++;
  for(n=1;n<=m;n<<=1);
  getsqr(n,a,b);  b[0]++; upd(b[0]);

  for(int i=0;i<n;i++)a[i]=0;//<n is enough
  getinv(n,b,a);
  for(int i=1;i<=m;i++)a[i]<<=1,upd(a[i]);
  for(int i=1;i<=m;i++)printf("%d\n",a[i]);
  return 0;
}

原文地址:https://www.cnblogs.com/Narh/p/10043272.html

时间: 2024-10-09 07:21:44

bzoj 3625(CF 438E)The Child and Binary Tree——多项式开方的相关文章

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上每个点

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\),+

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

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——生成函数

题面 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$ 求根公式

CF(437C)The Child and Toy(贪心)

题意:给出一个无向图,每个点有点权,操作是一个一个将所有点揪走直至剩下一个点,揪走一个点的代价是剩下点中与其连边的点的点权和.求完成操作所需花费的最小代价. 解法:贪心的思想,每次将剩余点中点权最大的点揪出,这样可以保证每条边都是会选择相对小的点权被消耗掉.所以直接输出所有边的边权和即可. 代码: /****************************************************** * author:xiefubao **************************

CF(438D) The Child and Sequence(线段树)

题意:对数列有三种操作: Print operation l,?r. Picks should write down the value of . Modulo operation l,?r,?x. Picks should perform assignment a[i]?=?a[i] mod x for each i (l?≤?i?≤?r). Set operation k,?x. Picks should set the value of a[k] to x (in other words

LeetCode Binary Tree Inorder Traversal

LeetCode解题之Binary Tree Inorder Traversal 原题 不用递归来实现树的中序遍历. 注意点: 无 例子: 输入: {1,#,2,3} 1 2 / 3 输出: [1,3,2] 解题思路 通过栈来实现,从根节点开始,不断寻找左节点,并把这些节点依次压入栈内,只有在该节点没有左节点或者它的左子树都已经遍历完成后,它才会从栈内弹出,这时候访问该节点,并它的右节点当做新的根节点一样不断遍历. AC源码 # Definition for a binary tree node

Binary Tree Longest Consecutive Sequence

Given a binary tree, find the length of the longest consecutive sequence path. The path refers to any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The longest consecutive path need to be from p