题意:
从n个连续的球中选出m组,每组也要连续且最多有两个,问方案数。
题解:
暴力dp很好写:
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
]
因为n很大,m很小,所以可以考虑倍增+卷积。
所以只要能够从生成函数
fi
f
i
得到
f2∗i
f
2
∗
i
就很愉快了。
直接将
f2i
f
i
2
漏了一种情况,就是少了同时跨过两部分的情况,所以还要加上
f2i−1
f
i
−
1
2
那么考虑怎么维护
f2∗i−1
f
2
∗
i
−
1
。
直观上是
fi−1∗fi∗2
f
i
−
1
∗
f
i
∗
2
,然而显然会有重复的情况。
考虑什么情况不会有重复计算的问题,就是当中间的三个球有两个球在同一段。
那么减去剩下的情况就好了,讨论中间的球选不选。
code:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#define LL long long
using namespace std;
LL T,n,bin[70000],m;
const LL p=998244353,yg=3;
LL pow(LL a,LL b)
{
LL ans=1;
while(b)
{
if(b&1) ans=ans*a%p;
a=a*a%p;b>>=1;
}
return ans;
}
LL g[70000],f[70000],N;
void ntt(LL *a,LL n,LL op)
{
for(LL i=0;i<n;i++) bin[i]=(bin[i>>1]>>1)|((i&1)*(n>>1));
for(LL i=0;i<n;i++) if(i<bin[i]) swap(a[i],a[bin[i]]);
for(LL i=1;i<n;i<<=1)
{
LL wn=pow(yg,op==1?(p-1)/(2*i):(p-1)-(p-1)/(2*i)),w,t;
for(LL j=0;j<n;j+=i<<1)
{
w=1;
for(LL k=0;k<i;k++)
{
t=a[i+j+k]*w%p;w=w*wn%p;
a[i+j+k]=(a[j+k]-t+p)%p;a[j+k]=(a[j+k]+t)%p;
}
}
}
if(op==-1)
{
LL Inv=pow(n,p-2);
for(LL i=0;i<n;i++) a[i]=a[i]*Inv%p;
}
}
LL A[70000],B[70000];
LL t1[70000],t2[70000],t3[70000];
void mul()
{
for(LL i=0;i<N;i++) A[i]=B[i]=0;
for(LL i=0;i<=m;i++) A[i]=g[i],B[i]=f[i];
ntt(A,N,1);ntt(B,N,1);
for(LL i=0;i<N;i++) t1[i]=A[i]*A[i]%p,t2[i]=B[i]*B[i]%p,t3[i]=A[i]*B[i]*2%p;
ntt(t1,N,-1);ntt(t2,N,-1);ntt(t3,N,-1);
for(LL i=1;i<=m;i++) g[i]=(t1[i]+t2[i-1])%p,f[i]=(t3[i]-t2[i-1]-t2[i]+p+p)%p;
}
void insert()
{
for(LL i=m;i>=1;i--)
f[i]=g[i],g[i]=(g[i]+f[i-1]+g[i-1])%p;
f[0]=g[0]=1;
}
int main()
{
scanf("%lld %lld",&n,&m);
N=1;while(N<=2*m) N<<=1;
LL b=0;g[0]=1;
for(LL i=1<<30;i;i>>=1)
{
if(b) mul(),b<<=1;
if(n&i) insert(),b|=1;
}
for(LL i=1;i<=m;i++) printf("%lld ",g[i]);
}