题意:
设 f ( n , k ) f(n,k) f(n,k)为有 k k k个 b i t bit bit子串的仅有小写字母构成的长度为 n n n字符串的种数。给定一个整数 n n n,求 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)为恰好有 k k k个子串 b i t bit bit的数量,要求他,组合数学的角度显然得令其中 k k k个位 b i t bit bit,我们把三位缩成一位考虑,那么就是在 n − 2 k n-2k n−2k位内挑选 b b b字符的位置,即 C n − 2 k k C_{n-2k}^{k} Cn−2kk,其他字符不好确定没有 b i t bit bit的情况,因此利用容斥,先计算至少含有 k k k个 b i t bit bit的数量,那么就是挑选的了 k k k个 b b b字符的位置,然后剩下的位置任意放,即 C n − 2 k k 2 6 n − 3 k C_{n-2k}^{k}26^{n-3k} Cn−2kk26n−3k。
但是我们这种其实是钦定了 k k k个 b i t bit bit,其他位置仍然可能有 b i t bit bit,并且会对恰好有 k + i k+i k+i个 b i t bit bit的数量重复计算了 C k + i k C_{k+i}^{k} Ck+ik次。
证明:任选一个恰好 k + i k+i k+i个 b i t bit bit的方案,任意选择 k k k个 b i t bit bit作为我们钦定的,实际上钦定方式没有差别,所以重复计算了有多少种 k k k个 b i t bit bit可以选择,即 C k + i k C_{k+i}^{k} Ck+ik。我们知道了那些方案重复计算了多少次,那么我们就可以写出递推式: f ( k ) = C n − 2 k k 2 6 n − 3 k − ∑ i = k + 1 n / 3 C i k f ( 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 = k n / 3 C i k f ( i ) = C n − 2 k k 2 6 n − 3 k \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 = n m C i n g ( i ) ⟺ g ( n ) = ∑ i = n m C i n ( − 1 ) i − n f ( 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 = k n / 3 ( − 1 ) i − k C i k C n − 2 i i 2 6 n − 3 i f(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 = k n / 3 ( − 1 ) i − k ( n − 2 i ) ! 2 6 n − 3 i k ! ( n − 3 i ) ! ( 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 ) k k ! ∑ i = k n / 3 ( − 1 ) i ( − 1 ) i ( n − 2 i ) ! 2 6 n − 3 i ( n − 3 i ) ! ( 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项,我们考虑凑定值卷积,并且我们凑的这个定值要是非负的
考虑令 P i = 1 ( − i ) ! , Q i = ( − 1 ) i ( n − 2 i ) ! 2 6 n − 3 i ( n − 3 i ) 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 ) k k ! ∑ i = k n / 3 P k − i Q i =\frac{(-1)^{k}}{k!}\sum_{i=k}^{n/3}P_{k-i}Q_{i} =k!(−1)k∑i=kn/3Pk−iQi,但由于 k − i k-i k−i是负数,数组存不了,我们考虑将 P P P平移一下:
重新令 P i = 1 ( n 3 − i ) ! P_{i}=\frac{1}{(\frac{n}{3}-i)!} Pi=(3n−i)!1, Q Q Q不变
那么原式 = ( − 1 ) k k ! ∑ i = k n / 3 P n 3 + k − i Q i =\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 , Q P,Q P,Q卷一起的第 n 3 + k \frac{n}{3}+k 3n+k项,不要误以为是挑几项出来乘积
目前遇到的 n t t ntt ntt模型:
1.和为定值卷积,卷积结果第 i i i项就是 ∑ j = 0 i a j b i − j \sum_{j=0}^{i}a_{j}b_{i-j} ∑j=0iajbi−j
2.出现了定值卷积但是是前面的推后面的递推形式, c d q cdq cdq分治 n t t ntt ntt,并且在分治到 l = = r l==r l==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;
}