2022牛客#2 E 字符串由多少特定子串的计数,容斥,二项式反演,ntt加速

题意:

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 n2k位内挑选 b b b字符的位置,即 C n − 2 k k C_{n-2k}^{k} Cn2kk,其他字符不好确定没有 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} Cn2kk26n3k

但是我们这种其实是钦定了 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)=Cn2kk26n3ki=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)=Cn2kk26n3k

由二项式反演: 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)inf(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)ikCikCn2ii26n3i

展开得到:

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)ikk!(n3i)!(ik)!(n2i)!26n3i

先把求和内的系数分离,得到

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)ki=kn/3(1)i(n3i)!(ik)!(1)i(n2i)!26n3i

剩下的计算繁琐,但是注意到存在 − 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=(n3i)(1)i(n2i)!26n3i

那么原式 = ( − 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)ki=kn/3PkiQi,但由于 k − i k-i ki是负数,数组存不了,我们考虑将 P P P平移一下:

重新令 P i = 1 ( n 3 − i ) ! P_{i}=\frac{1}{(\frac{n}{3}-i)!} Pi=(3ni)!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)ki=kn/3P3n+kiQi,可以利用卷积

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=0iajbij

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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值