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(x)*B(x))

多项式开方:

求B(x),使得B(x)*B(x)=A(x) (mod x^m)

继续考虑倍增。

假设我们已知B(x)*B(x)=A(x) (mod x^m),要求C(x),使得C(x)*C(x)=A(x) (mod x^(2m))

简单分析可得C(x)=(B(x)^2+A(x))/(2B(x)),需要求B(x)的逆。

观察到以上两个式子都用到了多项式乘法,又因为在模意义下,我们需要NTT。

NTT和FFT的区别在于单位根不一样,NTT需要找一个p的原根,而且通常要求p是形如k*2^q+1的一个质数。

找原根暴力找就行了。

(这题卡常数,所以我都用的int)

#include <cstdio>
#include <cstring>
#include <algorithm>

typedef long long ll;
const int N=300000,p=998244353;
int n,m,x,ni,r[N],h[N],_h[N],__h[N],t[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=2;i<=n;i<<=1)
    for(int j=0,wn=pw(3,((p-1)/i*f+p-1)%(p-1)),m=i>>1;j<n;j+=i)
    for(int k=0,w=1;k<m;k++,w=(ll)w*wn%p) {
        int x=a[j+k],y=(ll)a[j+k+m]*w%p;
        a[j+k]=(x+y)%p,a[j+k+m]=(x-y+p)%p;
    }
    if(f==-1) {
        ll ni=pw(n,p-2);
        for(int i=0;i<n;i++) a[i]=(ll)a[i]*ni%p;
    }
}
void gt2(int n) {
    if(n==1) {__h[0]=pw(_h[0],p-2); return;}
    gt2(n>>1),memcpy(t,_h,sizeof(int)*n),memset(t+n,0,sizeof(int)*n);
    int m=1,l=-1;
    while(m<n<<1) m<<=1,l++;
    for(int i=0;i<m;i++) r[i]=(r[i>>1]>>1)|((i&1)<<l);
    ntt(t,m,1),ntt(__h,m,1);
    for(int i=0;i<m;i++) __h[i]=(ll)__h[i]*(2-(ll)t[i]*__h[i]%p+p)%p;
    ntt(__h,m,-1),memset(__h+n,0,sizeof(int)*n);
}
void gt(int n) {
    if(n==1) {_h[0]=1; return;}
    gt(n>>1),memset(__h,0,sizeof(int)*n),gt2(n);
    memcpy(t,h,sizeof(int)*n),memset(t+n,0,sizeof(int)*n);
    int l=-1,m=1;
    while(m<n<<1) m<<=1,l++;
    for(int i=0;i<m;i++) r[i]=(r[i>>1]>>1)|((i&1)<<l);
    ntt(t,m,1),ntt(_h,m,1),ntt(__h,m,1);
    for(int i=0;i<m;i++) _h[i]=((ll)_h[i]*_h[i]+t[i])%p*__h[i]%p*ni%p;
    ntt(_h,m,-1),memset(_h+n,0,sizeof(int)*n);
}

int main() {
    scanf("%d%d",&m,&n),ni=pw(2,p-2);
    for(int i=1;i<=m;i++) scanf("%d",&x),h[x]++;
    for(int i=1;i<=n;i++) h[i]=(-h[i]*4+p)%p;
    for(m=n,n=1;n<=m;n<<=1);
    h[0]++,gt(n),_h[0]=(_h[0]+1)%p,memset(__h,0,sizeof __h),gt2(n);
    for(int i=1;i<=m;i++) printf("%d\n",__h[i]*2%p);
    return 0;
}
时间: 2024-10-19 20:26:38

NTT+多项式求逆+多项式开方(BZOJ3625)的相关文章

[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

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

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

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

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

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

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

【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个数划分

【bzoj3456】城市规划 容斥原理+NTT+多项式求逆

题目描述 求出n个点的简单(无重边无自环)无向连通图数目mod 1004535809(479 * 2 ^ 21 + 1). 输入 仅一行一个整数n(<=130000) 输出 仅一行一个整数, 为方案数 mod 1004535809. 样例输入 3 样例输出 4 题解 容斥原理+NTT+多项式求逆 设 $f_i$ 表示 $i$ 个点的简单无向连通图的数目,$g_i$ 表示 $i$ 个点的简单无向图的数目. 根据定义得 $g_i=2^{\frac{n(n-1}2}$ . 对于 $f_i$ ,考虑容斥

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

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&