BZOJ3684 大朋友和多叉树(多项式相关计算)

设$f(x)$为树的生成函数,即$x^i$的系数为根节点权值为$i$的树的个数。
不难得出$f(x)=\sum_{k\in D}f(x)^k+x$
我们要求这个多项式的第$n$项,由拉格朗日反演可得
$[x^n]f(x)=\frac1n[x^{n-1}](\frac x{g(x)})^n$
其中$[x^n]f(x)$表示$f(x)$的$n$次项系数。
$f(x)$是$g(x)$的复合逆,即$g(f(x))=x$
在本题中,$g(x)=x-\sum_{k\in D}x^k$
我们需要多项式求逆和多项式快速幂。
多项式求逆就不介绍了,多项式快速幂一种朴素的做法是倍增+NTT,复杂度是$O(n\log n\log k)$
有没有更快的做法呢?
观察到$f(x)^n=e^{n\ln(f(x))}$,所以我们只需要快速算$\ln(f(x))$及$e^{f(x)}$即可。
注意$f(x)$的常数项要为1,还好出题人良心保证了这一点。
Part 1:如何算$\ln(f(x))$?
设$g(x)=\ln(f(x))$,那么$g‘(x)=\frac{f‘(x)}{f(x)}$,所以$g(x)=\int\frac{f‘(x)}{f(x)}$,时间复杂度$O(n\log n)$
Part 2:如何算$e^{f(x)}$?
还是考虑倍增,假设我已经求出$g_0(x)=e^{f(x)}(mod\;x^n)$,要求$g(x)=e^{f(x)}(mod\;x^{2n})$
根据泰勒展开,有$$0=h(g(x))=\sum_{i=0}^{\infty}\frac{h^{(i)}(g_0(x))}{i!}(g(x)-g_0(x))^i$$当$i>1$时,上式$mod\;x^{2n}$为$0$
所以$0=h(g_0(x))+h‘(g_0(x))(g(x)-g_0(x))\;(mod\;x^{2n})$
即$g(x)=g_0(x)-\frac{h(g_0(x))}{h‘(g_0(x))}(mod\;x^{2n})$
其中$h(g(x))=\ln(g(x))-f(x)$
所以$g(x)=g_0(x)-\frac{\ln(g_0(x))-f(x)}{\frac 1{g_0(x)}}=g_0(x)(1-\ln(g_0(x))+f(x))\;(mod\;x^{2n})$
时间复杂度$O(n\log n)$

#include <cstdio>
#include <cstring>
#include <algorithm>
#define pre m=n<<1; for(int i=0;i<m;i++) r[i]=(r[i>>1]>>1)|((i&1)<<l)

typedef long long ll;
const int p=950009857,N=300000;
int n,m,l,x,nn,f[N],g[N],t1[N],t2[N],t3[N],r[N],ni[N];
ll pw(ll a,int b) {ll r=1; for(;b;b>>=1,a=a*a%p) if(b&1) r=r*a%p; return r;}

void ntt(int *a,int n,int f) {
    for(int i=0;i<n;i++) if(r[i]>i) std::swap(a[i],a[r[i]]);
    for(int i=1;i<n;i<<=1)
    for(int j=0,wn=pw(7,((p-1)/(i*2)*f+p-1)%(p-1));j<n;j+=i<<1)
    for(int k=0,w=1;k<i;k++,w=(ll)w*wn%p) {
        int x=a[j+k],y=(ll)a[j+k+i]*w%p;
        a[j+k]=(x+y)%p,a[j+k+i]=(x-y+p)%p;
    }
    if(!~f) for(int i=0;i<n;i++) a[i]=(ll)a[i]*ni[n]%p;
}
void inv(int *f,int *g,int *t,int n,int l) {
    if(n==1) {g[0]=pw(f[0],p-2); return;}
    inv(f,g,t,n>>1,l-1),memcpy(t,f,sizeof(int)*n),memset(t+n,0,sizeof(int)*n),pre;ntt(t,m,1),ntt(g,m,1);
    for(int i=0;i<m;i++) g[i]=(ll)g[i]*(2-(ll)t[i]*g[i]%p+p)%p;
    ntt(g,m,-1),memset(g+n,0,sizeof(int)*n);
}
void ln(int *f,int *g,int *t,int n,int l) {
    memset(t,0,sizeof(int)*n*2),inv(f,t,t1,n,l);
    for(int i=0;i+1<n;i++) g[i]=(ll)f[i+1]*(i+1)%p;
    pre;ntt(g,m,1),ntt(t,m,1);
    for(int i=0;i<m;i++) g[i]=(ll)g[i]*t[i]%p;
    ntt(g,m,-1);
    for(int i=m-1;i;i--) g[i]=(ll)g[i-1]*ni[i]%p;
    g[0]=0,memset(g+n,0,sizeof(int)*n);
}
void ex(int *f,int *g,int *t,int n,int l) {
    if(n==1) {g[0]=1; return;}
    ex(f,g,t,n>>1,l-1),memset(t,0,sizeof(int)*n*2),ln(g,t,t2,n,l);
    for(int i=0;i<n;i++) t[i]=(f[i]-t[i]+p)%p;
    t[0]=(t[0]+1)%p,pre;ntt(t,m,1),ntt(g,m,1);
    for(int i=0;i<m;i++) g[i]=(ll)g[i]*t[i]%p;
    ntt(g,m,-1),memset(g+n,0,sizeof(int)*n);
}

int main() {
    scanf("%d%d",&n,&m),f[0]++,ni[1]=1,nn=n;
    for(int i=1;i<=m;i++) scanf("%d",&x),f[x-1]=p-1;
    for(m=n,n=1,l=0;n<=m;n<<=1) l++;
    for(int i=2;i<=n*2;i++) ni[i]=(ll)(p-p/i)*ni[p%i]%p;
    inv(f,g,t1,n,l),memset(f,0,sizeof f),ln(g,f,t2,n,l);
    for(int i=0;i<n;i++) f[i]=(ll)f[i]*nn%p;
    memset(g,0,sizeof g),ex(f,g,t3,n,l),printf("%lld",(ll)g[nn-1]*ni[nn]%p);
    return 0;
}
时间: 2024-12-16 11:49:37

BZOJ3684 大朋友和多叉树(多项式相关计算)的相关文章

[BZOJ3684]大朋友和多叉树

[BZOJ3684]大朋友和多叉树 试题描述 我们的大朋友很喜欢计算机科学,而且尤其喜欢多叉树.对于一棵带有正整数点权的有根多叉树,如果它满足这样的性质,我们的大朋友就会将其称作神犇的:点权为 \(1\) 的结点是叶子结点:对于任一点权大于 \(1\) 的结点 \(u\),\(u\) 的孩子数目 \(deg[u]\) 属于集合 \(D\),且 \(u\) 的点权等于这些孩子结点的点权之和. 给出一个整数 \(s\),你能求出根节点权值为 \(s\) 的神犇多叉树的个数吗?请参照样例以更好的理解什

BZOJ 3684 大朋友和多叉树 FFT+拉格朗日反演

题目大意:给定n和集合S,求满足下列要求的多叉树的个数: 1.每个非叶节点的子节点数量在集合S中 2.每个叶节点的权值为1,每个非叶节点的权值为子节点权值之和 3.根节点的权值为n 注意每个节点的子节点有顺序 令fi表示根节点权值为i的神犇二叉树个数,F(x)为fi的生成函数,C(x)为S的生成函数,那么有: F(x)=∑i∈SFi(x)+x F(x)=C(F(x))+x F(x)?C(F(x))=x 不妨令G(x)=1?C(x) 那么有: G(F(x))=x 因此F(x)是G(x)的复合逆 拉

洛谷 P2008 大朋友的数字

P2008 大朋友的数字 题目背景 在NOIP2013的赛场上,常神牛华丽丽的手残了,小朋友的数字一题只得了10分.于是,他要恶搞一下这道题. 题目描述 有一批大朋友(年龄15岁以上),他们每人手上拿着一个数字,当然这个数字只有1位,也就是0到9之间.每个大朋友的分数为在他之前的最长不下降子序列中所有数之和.(这个序列必须以它作为结尾!)如有多个最长不下降子序列,那么取编号字典序最小的.现在告诉你有n个大朋友,以及他们各自的数字,请你求出他们每个人的分数. 输入输出格式 输入格式: 输入文件为b

圆的相关计算

package 练习; import java.awt.BorderLayout;import java.awt.Color;import java.awt.Font; import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JPanel;import java.awt.GridLayout;import java.awt.TextArea;import j

mysql数据导出到excel以及相关计算

mysql 查询出数据之后, 可以选择导出文件 默认是csv文件  如果是整数类型的数据 可以CONCAT('\'', filed) 多加个'就可以变成文本了, 然后以文本编辑器打开csv文件 把'等字符替换为空字符串. 新建一个excel文件,找到数据-->自文本,选择下一步 格式为文本  确定即可 一些基本的excel函数: 计算某个字符或数字等的个数:=COUNTIF(A:A,"测试") 就是在A列的'测试'这个字符串出现的个数 根据身份证号计算性别:=IF(MOD(MID

【算法总结】多项式相关

[快速傅里叶变换] [相关资料] <虚数的图解>        <虚数的意义>        <FFT学习笔记> <从多项式乘法到快速傅里叶变换>        <Fast Fourier Transform> <[快速傅里叶变换][FFT][WikiOI][P3132][高精度练习之超大整数乘法]> [模板代码] [FFT] 1 const double pi=acos(-1); 2 struct cpx{double r,i;cp

多项式相关——FFT(学习中……持续更新)

FFT 参考blog: 十分简明易懂的FFT(快速傅里叶变换) 快速傅里叶变换(FFT)详解 系数表示法 一个一元\(n\)次多项式\(f(x)\)可以被表示为:\[f(x) = \sum_{i = 0}^{n}a_{i}x^{i}\] 即用\(i\)次项的系数来表示\(f(x)\),展开就是\(f(x) = {a_{0}, a_{1}...a_{n}}\) 点值表示法 把多项式看做一个函数,然后带入\(n\)个不同的\(x\),可以得到\(n\)个不同的\(y\),每对\((x, y)\)就组

大数据开发实战:数据流图及相关数据技术

1.大数据流程图 2.大数据各个环节主要技术 2.1.数据处理主要技术 Sqoop:(发音:skup)作为一款开源的离线数据传输工具,主要用于Hadoop(Hive) 与传统数据库(MySql,PostgreSQL)间的数据传递.它可以将一个关系数据库中数据导入Hadoop的HDFS中, 也可以将HDFS中的数据导入关系型数据库中. Flume:实时数据采集的一个开源框架,它是Cloudera提供的一个高可用用的.高可靠.分布式的海量日志采集.聚合和传输的系统.目前已经是Apache的顶级子项目

DT大数据梦工厂Spark机器学习相关视频资料

大数据未来几年发展的重点方向,大数据战略已经在十八届五中全会上作为重点战略方向,中国在大数据方面才刚刚起步,但是在美国已经产生了上千亿的市场价值.举个例子,美国通用公司是一个生产飞机发动机的一个公司,这家公司在飞机发动机的每一个零部件上都安装了传感器,这些传感器在飞机发动机运作的同时不断的把发动机状态的数据传到通用公司的云平台上,通用公司又有很多数据分析中心专门接受这些数据,根据大数据的分析可以随时掌握每一家航空公司发动机的飞行状况,可以告知这些航空公司发动机的哪些部件需要检修或保养,避免飞机事