有一类数列递推是像卡特兰数一样我卷我自己类型的。
举一个例子:
这个怎么分治FFT?我们平常的分治FFT是 f=f*g类型的,做法是cdq分治,到一个区间 时先算出 ,再用 和 卷起来算出对 的贡献,再递归右边。
把这里的 直接换成 ?其实不可以,因为当我们需要用到 时有可能它还没算出。
那么考虑换一种思路。之前的“左边对右边的贡献”定义为 的下标属于左边的区间,但是现在式子里有两个 ,那么我们重新定义为两个下标较大的那个属于左边的区间。
这时就可以计算贡献了。
当区间的 时,较大的指针属于左边的区间那么较小的也就属于这个区间,只需要自己卷一下再统计贡献即可。
若区间的 ,则我们需要考虑它与前面区间的贡献了。一个结论是此时的 一定成立。考虑线段树的结构,它的左儿子区间大小只会大于等于右儿子。我们已经向右递归过的话,则左边的大小一定会大于等于右边的大小。于是此时也没有未知的量,直接卷一下统计贡献即可。
复杂度还是 。
#include<bits/stdc++.h>
using namespace std;
const int N=8e5+50,mod=998244353;
int r[N],lo[N],rt[N],irt[N],inv[N],n;
int Glim(int n){int z=1;while(z<n)z<<=1;return z;}
void getr(int n){for(int i=1;i<n;i++)r[i]=r[i>>1]>>1^(i&1)<<lo[n];}
int power(int x,int y){
int z=1;
for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)z=1ll*z*x%mod;
return z;
}
void init(int n){
int lim=Glim(n);
for(int i=2,j=0;i<=lim;i<<=1,j++)lo[i]=j,inv[i]=power(i,mod-2);
for(int i=1;i<lim;i<<=1){
int Wn=power(3,(mod-1)/(i*2));
for(int k=0,w=1;k<i;k++,w=1ll*w*Wn%mod)rt[i+k]=w;
}
for(int i=1;i<lim;i++)irt[i]=power(rt[i],mod-2);
}
void NTT(int *a,int n,int op){
for(int i=0;i<n;i++)if(i>r[i])swap(a[i],a[r[i]]);
for(int i=1,*w=(op>0?rt:irt);i<n;i<<=1)
for(int j=0;j<n;j+=i<<1)
for(int k=0;k<i;k++){
int x=a[j+k],y=1ll*a[i+j+k]*w[i+k]%mod;
a[j+k]=(x+y)%mod;a[i+j+k]=(x-y)%mod;
}
if(op<0)for(int i=0;i<n;i++)a[i]=1ll*a[i]*inv[n]%mod;
}
int a[N],A[N],B[N];
void cdq(int l,int r){
if(l==r)return;
int mid=(l+r)>>1;
cdq(l,mid);
if(!l){//l*2+1<=r
int lim=Glim(2*(mid-l)+1);
for(int i=0;i<lim;i++)A[i]=i+l<=mid?a[i+l]:0;
getr(lim);NTT(A,lim,1);
for(int i=0;i<lim;i++)A[i]=1ll*A[i]*A[i]%mod;
NTT(A,lim,-1);
for(int i=max(2*l+1,mid+1);i<=r;i++)(a[i]+=A[i-2*l-1])%=mod;
}
else{
int lim=Glim((mid-l)+r-l);
for(int i=0;i<lim;i++)A[i]=i+l<=mid?a[i+l]:0,B[i]=i<r-l?a[i]:0;
getr(lim);NTT(A,lim,1);NTT(B,lim,1);
for(int i=0;i<lim;i++)A[i]=1ll*A[i]*B[i]%mod;
NTT(A,lim,-1);
for(int i=max(l+1,mid+1);i<=r;i++)a[i]=(a[i]+2ll*A[i-l-1])%mod;
}
cdq(mid+1,r);
}
int main(){
scanf("%d",&n);int st=clock();init(n*2);
a[1]=1;a[2]=1;cdq(0,n);
printf("%d %d\n",a[n],clock()-st);
return 0;
}
这个东西我开始不会分治fft,但却自己想出了牛顿迭代。。
把这个递推写成生成函数的形式:
然后移个项就是函数求零点的形式啦,直接牛顿迭代就好了。
#include<bits/stdc++.h>
using namespace std;
const int N=8e5+50,mod=998244353;
int r[N],lo[N],rt[N],irt[N],inv[N];
int Glim(int n){int z=1;while(z<n)z<<=1;return z;}
void getr(int n){for(int i=1;i<n;i++)r[i]=r[i>>1]>>1^(i&1)<<lo[n];}
int power(int x,int y){
int z=1;
for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)z=1ll*z*x%mod;
return z;
}
void init(int n){
int lim=Glim(n);
for(int i=2,j=0;i<=lim;i<<=1,j++)lo[i]=j,inv[i]=power(i,mod-2);
for(int i=1;i<lim;i<<=1){
int Wn=power(3,(mod-1)/(i*2));
for(int k=0,w=1;k<i;k++,w=1ll*w*Wn%mod)rt[i+k]=w;
}
for(int i=1;i<lim;i++)irt[i]=power(rt[i],mod-2);
}
void NTT(int *a,int n,int op){
for(int i=0;i<n;i++)if(i>r[i])swap(a[i],a[r[i]]);
for(int i=1,*w=(op>0?rt:irt);i<n;i<<=1)
for(int j=0;j<n;j+=i<<1)
for(int k=0;k<i;k++){
int x=a[j+k],y=1ll*a[i+j+k]*w[i+k]%mod;
a[j+k]=(x+y)%mod;a[i+j+k]=(x-y)%mod;
}
if(op<0)for(int i=0;i<n;i++)a[i]=1ll*a[i]*inv[n]%mod;
}
int c[N],d[N];
void Inv(int *a,int *b,int n){
if(n==1){b[0]=power(a[0],mod-2);return;}
int m=n+1>>1,lim=Glim(n+m);Inv(a,b,m);
for(int i=0;i<lim;i++)c[i]=i<n?a[i]:0,d[i]=i<m?b[i]:0;
getr(lim);NTT(c,lim,1);NTT(d,lim,1);
for(int i=0;i<lim;i++)c[i]=1ll*c[i]*d[i]%mod*d[i]%mod;
NTT(c,lim,-1);for(int i=m;i<n;i++)b[i]=-c[i];
}
int X[N],F[N],A[N],B[N];
void calc(int *a,int n){
if(n==1)return;
int m=n+1>>1,lim=Glim(n*2);calc(a,m);
for(int i=0;i<lim;i++)
X[i]=i==1,F[i]=i<m?a[i]:0,
A[i]=i<=m?2*a[i-1]%mod:0;
A[0]=-1;Inv(A,B,n);
for(int i=n;i<lim;i++)B[i]=0;
getr(lim);NTT(X,lim,1);NTT(F,lim,1);NTT(B,lim,1);
for(int i=0;i<lim;i++)A[i]=(X[i]*(1ll*F[i]*F[i]%mod+X[i]+1)%mod-F[i])*B[i]%mod;
NTT(A,lim,-1);
for(int i=m;i<n;i++)a[i]=-A[i];
}
int n,a[N],b[N];
int main(){
scanf("%d",&n);int st=clock();init((n+1)*2);
calc(a,n+1);
printf("%d %d",(a[n]+mod)%mod,clock()-st);
return 0;
}
直接想到牛迭是因为刚刚做了烷基计数,它是牛顿迭代。式子是这样的:
这个 和 好像很奇怪的样子,牛迭的时候不知道怎么处理。它确实是跟 有关呀,那要把它当做变量?并不,因为我们在 扩展到 时,F(x^2) 只用到了 以内的系数,而它们已经准确求出,所以现在 和 已经是完全准确,可以把它们看作常量多项式。正常牛迭就好了。(式子长得很恶心)
所以这种题一般可以转化为生成函数的话真的要试着转一转,因为分治FFT其实还是细节比较多的,(可能我比较菜)
然后烷烃计数(咕了)