“蔚来杯“2022牛客暑期多校训练营2 Falfa with Substring(二项式反演,ntt,组合数学)

题意为 定义 $$F_{n,k}$$ 为长度为n的字符串中出现k个bit的字符串数量

让我们求 $$F_{n,0},F_{n,1}.....F_{n,n}$$

首先我们想等于k个bit的数量不是很好想

如果我们能想出大于等于k的表达式,在进行容斥或反演,会简单一些

假设我们想求长度为n的字符串中大于等于k个bit的数量

我们可以利用组合数学的捆绑法----把k个bit捆绑在一起,这样一共有 n-3*k+k个位置

我们从中选出k个来放bit,剩下n-3*k个位置放任意字符,等于

\binom{n-2k}{k}*26^{n-3k}

记做 f(k)

则f(k)由恰好出现k次,k+1次....n次累加起来,在乘上组合系数,有

f(k)=\sum_{i=k}^{n} g(i)*\binom{i}{k}

这里特别解释一下为什么要乘上组合系数,因为我们之前计算f(k)的时候,假设我们计算的是

n=6,k=1,那么我们在计算bit***这个字符串的时候,实际上已经计算了一次bitbit

而我们计算***bit的时候,又算了一边bitbit,所以实际上我们算的f(k)是有重复的

而这个重复的影响就是这个组合系数

好我们得到这个式子时候,对他进行二项式反演(容斥),把它变成

g(k)=\sum_{i=k}^{n}(-1)^{i-k} *\binom{i}{k} *f(i)

接下来继续推倒,把组合数展开,可以得到

k!*g(k)=\sum_{i=k}^{n}i!*f(i)\frac{(-1)^{i-k} }{(i-k)!}

接下来令

F(i)=i!*f(i)

G(i)=\frac{(-1)^{i} }{i!}

ans(i)=i!*g(i)

ans(i)=\sum_{i=k}^{n}F(i)*G(i-k)

接下来我们只需要令右边i等于i+k

ans(i)=\sum_{i=0}^{n-k}F(i+k)*G(i)

我们知道卷积是

F(i)=\sum_{i=0}^{n}f(i)*g(n-i)

而上面n-k-i正好加上i+k等于n

所以考虑翻转G(i)

之后卷一卷

感谢凌乱之风的博客-CSDN博客让我搞懂了这道题

代码如下

#include <bits/stdc++.h>
#include <iomanip>
#define fer(i,a,b) for(int i=a;i<=b;i++)
#define der(i,a,b) for(int i=a;i>=b;i--)
#define int long long
#define len(x) ((int)x.size())
#define pb push_back
#define pll pair<int,int>
#define ld double
#define x first
#define y second
using namespace std;
const int N=2e6+10;
int mod=998244353;
int n;
inline int add(int x, int y) { 
    x += y;
    if(x >= mod) x -= mod;
    return x;
}
inline int sub(int x, int y) { 
    x -= y;
    if(x < 0) x += mod;
    return x;
}
inline int mul(int x, int y) {
    return 1ll * x * y % mod;
}
inline int qpow(int x, int y) {
    int ret = 1;
    for(; y; y >>= 1, x = mul(x, x)) if(y & 1) ret = mul(ret, x);
    return ret;
}
const int G = 3;
const int Ginv = qpow(G, mod - 2);
int rev[N<<1];
inline void ntt(int *a, int n, int o) {
    for(int i = 0; i < n; i ++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) * (n >> 1));
    for(int i = 0; i < n; i ++) if(rev[i] > i) swap(a[i], a[rev[i]]);
    for(int len = 2; len <= n; len <<= 1) {
        int w0 = qpow((o == 1)? G : Ginv, (mod - 1) / len);
        for(int j = 0; j < n; j += len) {
            int wn = 1;
            for(int k = j; k < j + (len >> 1); k ++, wn = mul(wn, w0)) {
                int X = a[k], Y = mul(a[k + (len >> 1)], wn);
                a[k] = add(X, Y), a[k + (len >> 1)] = sub(X, Y);
            }   
        }
    }
    int ninv = qpow(n, mod - 2);
    if(o == -1)
        for(int i = 0; i < n; i ++) a[i] = mul(a[i], ninv);
}
#define clr(a, n) (memset(a, 0, sizeof(int) * n))
 
int aa[N << 1], bb[N << 1], lim=362144*4;
inline vector<int> operator * (const vector<int> & A, const vector<int> & B) {
    for(int i = 0; i < len(A); i ++) aa[i] = A[i];
    for(int i = 0; i < len(B); i ++) bb[i] = B[i];
    vector<int> C;
    C.resize(min(lim, len(A) + len(B) - 1));
    int len = 1;
    for(; len <= len(A) + len(B) - 1; len <<= 1);
    ntt(aa, len, 1), ntt(bb, len, 1);
    for(int i = 0; i < len; i ++) aa[i] = mul(aa[i], bb[i]);
    ntt(aa, len, -1);
    for(int i = 0; i < len(C); i ++) C[i] = aa[i];
    clr(aa, len), clr(bb, len);
    return C;
}
template <typename _Tp>void input(_Tp &x){
    char ch(getchar());bool f(false);while(!isdigit(ch))f|=ch==45,ch=getchar();
    x=ch&15,ch=getchar();while(isdigit(ch))x=x*10+(ch&15),ch=getchar();
    if(f)x=-x;
}
template <typename _Tp,typename... Args>void input(_Tp &t,Args &...args){input(t);input(args...);}

int fc[N],gc[N];
void init()
{
    fc[0]=1;
    for(int i=1;i<=n;i++) fc[i]=fc[i-1]*i%mod;
    gc[n]=qpow(fc[n],mod-2);
    for(int i=n-1;i>=0;i--) gc[i]=gc[i+1]*(i+1)%mod;
}
int C(int n,int m)
{
    return fc[n]*gc[m]%mod*gc[n-m]%mod;
}
int f(int k){
    int res=C(n-2*k,k)*qpow(26,n-3*k);
    return res%mod;
}
int F(int k){
    return (f(k)*fc[k])%mod;
}
int Gi(int k){
    return (qpow(mod-1,k)*gc[k])%mod;
}
vector<int> ff,gg;
signed main()
{
    cin>>n;
    init();
    fer(i,0,n){
        if(n>=3*i){
            ff.pb(F(i));
        }
        else{
            ff.pb(0);
        }
    }
    fer(i,0,n){
        gg.pb(Gi(i));
    }
    reverse(gg.begin(),gg.end());
    vector<int> res;
    res=ff*gg;
    res.resize(n*2+1);
    fer(i,0,n){
        cout<<(res[i+n]*gc[i])%mod<<' ';
    }
    cout<<"\n";
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值