天天看點

【CF755G】PolandBall and Many Other Balls(倍增FFT)

有$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;
}
           

敗得義無反顧,弱得一無是處

繼續閱讀