题意:
设f(n,k)f(n,k)f(n,k)为有kkk个bitbitbit子串的仅有小写字母构成的长度为nnn字符串的种数。给定一个整数nnn,求f(n,0),f(n,1),..,f(n,n)f(n,0),f(n,1),..,f(n,n)f(n,0),f(n,1),..,f(n,n)
方法:
(容斥原理,组合计数)
设f(k)f(k)f(k)为恰好有kkk个子串bitbitbit的数量,要求他,组合数学的角度显然得令其中kkk个位bitbitbit,我们把三位缩成一位考虑,那么就是在n−2kn-2kn−2k位内挑选bbb字符的位置,即Cn−2kkC_{n-2k}^{k}Cn−2kk,其他字符不好确定没有bitbitbit的情况,因此利用容斥,先计算至少含有kkk个bitbitbit的数量,那么就是挑选的了kkk个bbb字符的位置,然后剩下的位置任意放,即Cn−2kk26n−3kC_{n-2k}^{k}26^{n-3k}Cn−2kk26n−3k。
但是我们这种其实是钦定了kkk个bitbitbit,其他位置仍然可能有bitbitbit,并且会对恰好有k+ik+ik+i个bitbitbit的数量重复计算了Ck+ikC_{k+i}^{k}Ck+ik次。
证明:任选一个恰好k+ik+ik+i个bitbitbit的方案,任意选择kkk个bitbitbit作为我们钦定的,实际上钦定方式没有差别,所以重复计算了有多少种kkk个bitbitbit可以选择,即Ck+ikC_{k+i}^{k}Ck+ik。我们知道了那些方案重复计算了多少次,那么我们就可以写出递推式:f(k)=Cn−2kk26n−3k−∑i=k+1n/3Cikf(i)f(k)=C_{n-2k}^{k}26^{n-3k}-\sum_{i=k+1}^{n/3}C_{i}^{k}f(i)f(k)=Cn−2kk26n−3k−∑i=k+1n/3Cikf(i)。
把函数写在一边,得到∑i=kn/3Cikf(i)=Cn−2kk26n−3k\sum_{i=k}^{n/3}C_{i}^{k}f(i)=C_{n-2k}^{k}26^{n-3k}∑i=kn/3Cikf(i)=Cn−2kk26n−3k。
由二项式反演:f(n)=∑i=nmCing(i) ⟺ g(n)=∑i=nmCin(−1)i−nf(i)f(n)=\sum_{i=n}^{m}C_{i}^{n}g(i) \iff g(n)=\sum_{i=n}^{m}C_{i}^{n}(-1)^{i-n}f(i)f(n)=∑i=nmCing(i)⟺g(n)=∑i=nmCin(−1)i−nf(i)
得到:f(k)=∑i=kn/3(−1)i−kCikCn−2ii26n−3if(k)=\sum_{i=k}^{n/3}(-1)^{i-k}C_{i}^{k}C_{n-2i}^{i}26^{n-3i}f(k)=∑i=kn/3(−1)i−kCikCn−2ii26n−3i
展开得到:
f(k)=∑i=kn/3(−1)i−k(n−2i)!26n−3ik!(n−3i)!(i−k)!f(k)=\sum_{i=k}^{n/3}(-1)^{i-k}\frac{(n-2i)!26^{n-3i}}{k!(n-3i)!(i-k)!}f(k)=∑i=kn/3(−1)i−kk!(n−3i)!(i−k)!(n−2i)!26n−3i
先把求和内的系数分离,得到
f(k)=(−1)kk!∑i=kn/3(−1)i(−1)i(n−2i)!26n−3i(n−3i)!(i−k)!f(k)=\frac{(-1)^{k}}{k!}\sum_{i=k}^{n/3}(-1)^{i}\frac{(-1)^{i}(n-2i)!26^{n-3i}}{(n-3i)!(i-k)!}f(k)=k!(−1)k∑i=kn/3(−1)i(n−3i)!(i−k)!(−1)i(n−2i)!26n−3i
剩下的计算繁琐,但是注意到存在−i-i−i与+i+i+i项,我们考虑凑定值卷积,并且我们凑的这个定值要是非负的
考虑令Pi=1(−i)!,Qi=(−1)i(n−2i)!26n−3i(n−3i)P_{i}=\frac{1}{(-i)!},Q_{i}=\frac{(-1)^{i}(n-2i)!26^{n-3i}}{(n-3i)}Pi=(−i)!1,Qi=(n−3i)(−1)i(n−2i)!26n−3i
那么原式=(−1)kk!∑i=kn/3Pk−iQi=\frac{(-1)^{k}}{k!}\sum_{i=k}^{n/3}P_{k-i}Q_{i}=k!(−1)k∑i=kn/3Pk−iQi,但由于k−ik-ik−i是负数,数组存不了,我们考虑将PPP平移一下:
重新令Pi=1(n3−i)!P_{i}=\frac{1}{(\frac{n}{3}-i)!}Pi=(3n−i)!1,QQQ不变
那么原式=(−1)kk!∑i=kn/3Pn3+k−iQi=\frac{(-1)^{k}}{k!}\sum_{i=k}^{n/3}P_{\frac{n}{3}+k-i}Q_{i}=k!(−1)k∑i=kn/3P3n+k−iQi,可以利用卷积
tips:
原式的求和就是所有P,QP,QP,Q卷一起的第n3+k\frac{n}{3}+k3n+k项,不要误以为是挑几项出来乘积
目前遇到的nttnttntt模型:
1.和为定值卷积,卷积结果第iii项就是∑j=0iajbi−j\sum_{j=0}^{i}a_{j}b_{i-j}∑j=0iajbi−j
2.出现了定值卷积但是是前面的推后面的递推形式,cdqcdqcdq分治nttnttntt,并且在分治到l==rl==rl==r时递推
#include<bits/stdc++.h>
#define ll long long
#define endl '\n'
using namespace std;
const long long mod=998244353,inv3=332748118;
ll qpow(ll a,ll b)
{
ll ret=1,base=a;
while(b)
{
if(b&1) ret=ret*base%mod;
base=base*base%mod;
b>>=1;
}
return ret;
}
int getlen(int k)
{
int ret=0;
while(k){k>>=1;ret++;}
return ret;
}
int getrev(int k,int len)
{
int ret=0;
while(k){ret=ret<<1|(k&1);len--;k>>=1;}
while(len--) ret<<=1;
return ret;
}
vector<int>pos(2100000);
inline void ntt(vector<ll>&a,int limit,int op)
{
for(int i=0;i<limit;i++)
if(i<pos[i]) swap(a[i],a[pos[i]]);
for(int len=2;len<=limit;len<<=1)
{
ll base=qpow(op==1?3ll:inv3,(mod-1)/len);
for(int l=0;l<limit;l+=len)
{
ll now=1;
for(int i=l;i<l+len/2;i++)
{
ll temp1=a[i],temp2=now*a[i+len/2]%mod;
a[i]=(temp1+temp2)%mod;
a[i+len/2]=(temp1-temp2+mod)%mod;
now=now*base%mod;
}
}
}
}
int n;
vector<ll>a(2100000),b(2100000);
ll fac[1000001],facinv[1000001],p[1000005];
inline void multi(int limit)
{
int len=getlen(limit-1);
for(int i=0;i<limit;i++) pos[i]=getrev(i,len);
ntt(a,limit,1);ntt(b,limit,1);
for(int i=0;i<limit;i++) a[i]=(a[i]*b[i])%mod;
ntt(a,limit,-1);
ll temp=qpow(limit,mod-2);
for(int i=0;i<limit;i++) a[i]=a[i]*temp%mod;
}
inline ll P(int x){return facinv[x];}
inline ll Q(int x){return (x&1?-1:1)*fac[n-2*x]*p[n-3*x]%mod*facinv[n-3*x]%mod;}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);cout.tie(nullptr);
cin>>n;fac[0]=p[0]=facinv[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=fac[i-1]*i%mod;
facinv[i]=qpow(fac[i],mod-2);
p[i]=p[i-1]*26%mod;
}
for(int i=0;i<=n/3;i++) a[i]=P(n/3-i),b[i]=Q(i);
// for(int i=1;i<=n;i++) printf("a[%d]=%lld\n",i,a[i]);
int limit=1;
while(limit<=2*n/3+2) limit<<=1;
multi(limit);
for(int i=0;i<=n/3;i++) cout<<(((i&1?-1:1)*a[i+n/3]*facinv[i])%mod+mod)%mod<<" ";
for(int i=n/3+1;i<=n;i++) cout<<0<<" ";
return 0;
}