我是靠谱客的博主 落后鞋子,最近开发中收集的这篇文章主要介绍CF755G-PolandBall and Many Other Balls【倍增FFT】正题,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

正题

题目链接:https://www.luogu.com.cn/problem/CF755G


题目大意

n n n个东西排成一排,每个组可以选择一个单独的物品或者两个连续的物品,一个物品不同同时在两个组里,但是可以不在组里。对于 i ∈ [ 1 , k ] iin[1,k] i[1,k]求分成 i i i组的方案数。

1 ≤ n ≤ 1 0 9 , 1 ≤ k < 2 15 1leq nleq 10^9,1leq k<2^{15} 1n109,1k<215


解题思路

有三种方法。

第一种是倍增 F F T FFT FFT,设 f i , j f_{i,j} fi,j表示到第 i i i个物品选了 j j j组时的方案数,那么设 F n ( x ) = ∑ i = 0 k f n , i x i F_n(x)=sum_{i=0}^kf_{n,i}x^i Fn(x)=i=0kfn,ixi
考虑把这个 F F F分成两半,然后考虑中间的选不选就是
F n + m ( x ) = F n ( x ) F m ( x ) + x F n − 1 ( x ) F m − 1 ( x ) F_{n+m}(x)=F_{n}(x)F_m(x)+xF_{n-1}(x)F_{m-1}(x) Fn+m(x)=Fn(x)Fm(x)+xFn1(x)Fm1(x)
我们发现如果需要计算 F 2 k F_{2^k} F2k,那么我们就需要维护 F 2 k − 1 , F 2 k − 1 − 1 , F 2 k − 1 − 2 F_{2^{k-1}},F_{2^{k-1}-1},F_{2^{k-1}-2} F2k1,F2k11,F2k12这三个东西。
但是这三个东西也可以用来计算 F 2 k − 1 , F 2 k − 2 F_{2^k-1},F_{2^k-2} F2k1,F2k2,所以可以维护这三个东西就行倍增。

然后处理的时候同理维护一个 F m F_{m} Fm F m − 1 F_{m-1} Fm1就好了。

时间复杂度 O ( n log ⁡ 2 n ) O(nlog^2 n) O(nlog2n),有点卡常

第二种方法是直接组合数学推导。将这个序列提出若干段,每一段之间间隔为 1 1 1,那么只有最末尾的段能够长度为 2 2 2的。
a n s k = ∑ i = 1 k ( n − i k ) ( k i ) ans_k=sum_{i=1}^kbinom{n-i}{k}binom{k}{i} ansk=i=1k(kni)(ik)
瓶颈在于后面的 ( k i ) binom{k}{i} (ik),也就是要求前后没有重复,所以我们可以考虑允许重复的容斥
⇒ a n s k = ∑ i = 1 k ( − 1 ) i ( k i ) ( n − i k − i ) 2 k − i Rightarrow ans_k=sum_{i=1}^k(-1)^{i}binom{k}{i}binom{n-i}{k-i}2^{k-i} ansk=i=1k(1)i(ik)(kini)2ki
⇒ ∑ i = 1 k ( − 1 ) i k ! i ! ( k − i ) ! ( n − i ) ! ( k − i ) ! ( n − k ) ! 2 k − i Rightarrow sum_{i=1}^k(-1)^ifrac{k!}{i!(k-i)!}frac{(n-i)!}{(k-i)!(n-k)!}2^{k-i} i=1k(1)ii!(ki)!k!(ki)!(nk)!(ni)!2ki
k ! ( n − k ) ! ∑ i = 1 k ( − 1 ) i ( n − i ) ! i ! × 2 k − i ( k − i ) ! 2 frac{k!}{(n-k)!}sum_{i=1}^kfrac{(-1)^i(n-i)!}{i!}times frac{2^{k-i}}{(k-i)!^2} (nk)!k!i=1ki!(1)i(ni)!×(ki)!22ki
就可以卷积了,时间复杂度 O ( n log ⁡ n ) O(nlog n) O(nlogn)

第三种方法是特征方程,回到第一个方法的 F n ( x ) F_n(x) Fn(x),我们有
f i , j = f i − 1 , j + f i − 1 , j − 1 + f i − 2 , j − 1 f_{i,j}=f_{i-1,j}+f_{i-1,j-1}+f_{i-2,j-1} fi,j=fi1,j+fi1,j1+fi2,j1
⇒ F n ( x ) = ( 1 + x ) F n − 1 ( x ) + x F n − 2 ( x ) Rightarrow F_n(x)=(1+x)F_{n-1}(x)+xF_{n-2}(x) Fn(x)=(1+x)Fn1(x)+xFn2(x)
这是一个二次项的递推式,过程就不在论述了,用特征方程化简可以得到
F n ( x ) = ( x + 1 − x 2 + 6 x + 1 2 ) n + 1 x 2 + 6 x + 1 ( m o d   x n + 1 ) F_{n}(x)=frac{(frac{x+1-sqrt {x^2+6x+1}}{2})^{n+1}}{sqrt{x^2+6x+1}}(mod x^{n+1}) Fn(x)=x2+6x+1 (2x+1x2+6x+1 )n+1(mod xn+1)
然后上全家桶就好了,时间复杂度也是 O ( n log ⁡ n ) O(nlog n) O(nlogn)

这里的标程用的是第一种方法。


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define int long long
using namespace std;
const int N=1<<16,P=998244353;
int n,k,m,r[N],f[3][N],t[3][N],g[2][N];
void fm(int &x){x+=x>>31&P;}
int power(int x,int b){
	int ans=1;
	while(b){
		if(b&1)ans=ans*x%P;
		x=x*x%P;b>>=1;
	}
	return ans;
}
void NTT(int *f,int op){
	for(int i=0;i<n;i++)
		if(i<r[i])swap(f[i],f[r[i]]);
	for(int p=2;p<=n;p<<=1){
		int tmp=power(3,(P-1)/p),len=p>>1;
		if(op==-1)tmp=power(tmp,P-2);
		for(int k=0;k<n;k+=p){
			int buf=1;
			for(int i=k,tt;i<(k|len);i++){
				tt=1ll*buf*f[i|len]%P;
				fm(f[i|len]=f[i]-tt);
				fm(f[i]=f[i]+tt-P);
				buf=1ll*buf*tmp%P;
			}
		}
	}
	if(op==-1){
		int invn=power(n,P-2);
		for(int i=0;i<n;i++)
			f[i]=1ll*f[i]*invn%P;
	}
	return;
}
void print(int x)
{if(x>9)print(x/10);putchar(x%10+'0');return;}
signed main()
{
	scanf("%d%d",&m,&k);k++;n=1;
	while(n<(k*2))n<<=1;
	for(int i=0;i<n;i++)
		r[i]=(r[i>>1]>>1)|((i&1)?(n>>1):0);
	f[0][0]=f[0][1]=f[1][0]=g[0][0]=1;
	for(int d=1;d<=m;d<<=1){
		if(m&d){
			for(int j=0;j<3;j++){
				for(int i=0;i<n;i++)
					t[j][i]=(i<k)?f[j][i]:0;
				NTT(t[j],1);
			}
			NTT(g[0],1);NTT(g[1],1);
			for(int i=0;i<n;i++){
				int b0=g[0][i],b1=g[1][i];
				g[0][i]=1ll*b0*t[0][i]%P;
				g[1][i]=1ll*b0*t[1][i]%P;
				t[0][i]=1ll*t[1][i]*b1%P;
				t[1][i]=1ll*t[2][i]*b1%P;
			}
			NTT(g[0],-1);NTT(g[1],-1);
			NTT(t[0],-1);NTT(t[1],-1);
			for(int i=0;i<k-1;i++)
				(g[0][i+1]+=t[0][i])%=P,
				(g[1][i+1]+=t[1][i])%=P;
			for(int i=k;i<n;i++)g[0][i]=g[1][i]=0;
		}
		if(d*2>m)break;
		for(int j=0;j<3;j++){
			for(int i=0;i<n;i++)
				t[j][i]=(i<k)?f[j][i]:0;
			NTT(t[j],1);
		}
		for(int i=0;i<n;i++){
			f[0][i]=1ll*t[0][i]*t[0][i]%P;
			f[1][i]=1ll*t[1][i]*t[0][i]%P;
			f[2][i]=1ll*t[1][i]*t[1][i]%P;
			t[0][i]=1ll*t[1][i]*t[1][i]%P;
			t[1][i]=1ll*t[1][i]*t[2][i]%P;
			t[2][i]=1ll*t[2][i]*t[2][i]%P;
		}
		for(int j=0;j<3;j++)
			NTT(f[j],-1),NTT(t[j],-1);
		for(int i=0;i<k-1;i++)
			(f[0][i+1]+=t[0][i])%P,
			(f[1][i+1]+=t[1][i])%P,
			(f[2][i+1]+=t[2][i])%P;
		for(int i=k;i<n;i++)f[0][i]=f[1][i]=f[2][i]=0;
	}
	for(int i=1;i<k;i++)
		print(g[0][i]),putchar(' ');
	return 0;
}

最后

以上就是落后鞋子为你收集整理的CF755G-PolandBall and Many Other Balls【倍增FFT】正题的全部内容,希望文章能够帮你解决CF755G-PolandBall and Many Other Balls【倍增FFT】正题所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(43)

评论列表共有 0 条评论

立即
投稿
返回
顶部