【CF755G】PolandBall and Many Other Balls(倍增FFT)
阿新 • • 發佈:2021-07-08
有$n$個物品擺成一排,定義一個組只能包含一個物品或相鄰兩個物品,一個物品最多被分入一個組。對於所有$i=1\sim k$,求選出$i$組的方案數。
的生成函式。
- 有\(n\)個物品擺成一排,定義一個組只能包含一個物品或相鄰兩個物品,一個物品最多被分入一個組。
- 對於所有\(i=1\sim k\),求選出\(i\)組的方案數。
- \(n\le10^9,k\le32767\)
暴力遞推
考慮設\(f_{n,i}\)表示從\(n\)個物品中選擇\(i\)組的方案數。
則暴力的遞推無非就是考慮第\(n\)個物品的狀態:不選(\(f_{n-1,i}\)),自成一組(\(f_{n-1,i-1}\)),與第\(n-1\)個物品湊一組(\(f_{n-2,i-1}\))。
由於\(n\)這麼大,容易想到倍增。
倍增\(FFT\)
令\(F_n(x)\)為\(f_n\)
對於每個\(i\),設\(n=2^i\),則我們需要維護好\(F_{2^i}(x),F_{2^i-1}(x),F_{2^i-2}(x)\)。
把物品分成兩部分,那麼有兩種情況:兩部分各自獨立或跨兩部分取了一組物品。
即:
\[F_{2^i}(x)=F_{2^{i-1}}(x)*F_{2^{i-1}}(x)+x*F_{2^{i-1}-1}(x)*F_{2^{i-1}-1}(x)\\ F_{2^i-1}(x)=F_{2^{i-1}}(x)*F_{2^{i-1}-1}(x)+x*F_{2^{i-1}-1}(x)*F_{2^{i-1}-2}(x)\\ F_{2^i-2}(x)=F_{2^{i-1}-1}(x)*F_{2^{i-1}-1}(x)+x*F_{2^{i-1}-2}(x)*F_{2^{i-1}-2}(x) \]倍增預處理一波即可。
程式碼:\(O(klog^2n)\)
敗得義無反顧,弱得一無是處#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Reg register #define RI Reg int #define Con const #define CI Con int& #define I inline #define W while #define K 32767 #define LG 30 #define X 998244353 #define Cl(f) memset(f,0,sizeof(f)) using namespace std; int n,k;struct Data {int f[K+5],g[K+5],h[K+5];I void Clear() {Cl(f),Cl(g),Cl(h);}}ans,res,s[LG+1]; I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;} namespace FastIO { #define FS 100000 #define pc(c) (FC==FE&&(clear(),0),*FC++=c) int OT;char FO[FS],OS[FS],*FC=FO,*FE=FO+FS; I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;} Tp I void write(Ty x,char y) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc(y);} }using namespace FastIO; namespace Poly { #define PR 3 #define IPR 332748118 int P,Inv,L,R[K<<2],F[K<<2],Af[K<<2],Ag[K<<2],Ah[K<<2],Bf[K<<2],Bg[K<<2],Bh[K<<2]; I void Init() { P=1,L=0;W(P<=(k<<1)) P<<=1,++L;for(RI i=0;i^P;++i) R[i]=((R[i>>1]>>1)|((i&1)<<L-1));Inv=QP(P,X-2); } I void NTT(int* s,CI op) { RI i,j,k,x,y,U,S;for(i=0;i^P;++i) i<R[i]&&(swap(s[i],s[R[i]]),0); for(i=1;i^P;i<<=1) for(U=QP(op,(X-1)/(i<<1)),j=0;j^P;j+=i<<1) for(S=1, k=0;k^i;++k,S=1LL*S*U%X) s[j+k]=((x=s[j+k])+(y=1LL*S*s[i+j+k]%X))%X,s[i+j+k]=(x-y+X)%X; if(op==IPR) for(i=0;i^P;++i) s[i]=1LL*s[i]*Inv%X; } I void Merge(Data& S,Con Data& A,Con Data& B)//合併A,B到S { RI i;for(i=0;i<=k;++i) Af[i]=A.f[i],Ag[i]=A.g[i],Ah[i]=A.h[i],Bf[i]=B.f[i],Bg[i]=B.g[i],Bh[i]=B.h[i]; for(;i^P;++i) Af[i]=Ag[i]=Ah[i]=Bf[i]=Bg[i]=Bh[i]=0; NTT(Af,PR),NTT(Ag,PR),NTT(Ah,PR),NTT(Bf,PR),NTT(Bg,PR),NTT(Bh,PR); for(i=0;i^P;++i) F[i]=1LL*Af[i]*Bf[i]%X;for(NTT(F,IPR),i=0;i<=k;++i) S.f[i]=F[i]; for(i=0;i^P;++i) F[i]=1LL*Af[i]*Bg[i]%X;for(NTT(F,IPR),i=0;i<=k;++i) S.g[i]=F[i]; for(i=0;i^P;++i) F[i]=1LL*Ag[i]*Bg[i]%X;for(NTT(F,IPR),i=0;i<=k;++i) S.h[i]=F[i],S.f[i]=(S.f[i]+F[i-1])%X; for(i=0;i^P;++i) F[i]=1LL*Ag[i]*Bh[i]%X;for(NTT(F,IPR),i=1;i<=k;++i) S.g[i]=(S.g[i]+F[i-1])%X; for(i=0;i^P;++i) F[i]=1LL*Ah[i]*Bh[i]%X;for(NTT(F,IPR),i=1;i<=k;++i) S.h[i]=(S.h[i]+F[i-1])%X; } } int main() { RI i;if(scanf("%d%d",&n,&k),Poly::Init(),!n) goto End;if(n==1) {ans.f[1]=1;goto End;}//特判n=0或1 for(s[1].h[0]=s[1].g[0]=s[1].g[1]=s[1].f[0]=s[1].f[2]=1,s[1].f[1]=3,i=2;i<=LG;++i) Poly::Merge(s[i],s[i-1],s[i-1]);//倍增預處理 for(ans=s[1],n-=2,i=LG;i;--i) n>>i&1&&(res=ans,ans.Clear(),Poly::Merge(ans,res,s[i]),0);//方便起見先初始化答案為F_2(x) if(n&1) for(i=k;i;--i) ans.f[i]=(0LL+ans.f[i]+ans.f[i-1]+ans.g[i-1])%X;//如果n是奇數,暴力轉移一次 End:for(i=1;i<=k;++i) write(ans.f[i]," \n"[i==k]);return clear(),0; }