2022牛客多校第四场C.Easy Counting Problem(EGF+NTT)

Posted 吃花椒的妙酱

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2022牛客多校第四场C.Easy Counting Problem(EGF+NTT)相关的知识,希望对你有一定的参考价值。

题意:给一个字符集,大小不超过10,第i个字符出现次数不少于ci,求字符串长度为n的方案数,至多300个询问,ci<=5000,n<=1e7
(不复制图片了,要钱的比赛不知道会不会被举办==)
思路:指数生成函数
由egf定义,第k个字符的egf为 f ( k ) = ∑ c k + o o x i i ! f(k)=\\sum_c_k^+oo\\fracx^ii! f(k)=ck+ooi!xi
则答案为 [ x n n ! ] ∏ k = 1 w f ( k ) , w 为字符集大小 [\\fracx^nn!]\\prod_k=1^wf(k),w为字符集大小 [n!xn]k=1wf(k)w为字符集大小
但是n比较大,直接卷肯定不行。改写一下f(k)的形式
f ( k ) = e x p ( x ) − ∑ i = 0 c k − 1 x i i ! f(k)=exp(x)-\\sum_i=0^c_k-1\\fracx^ii! f(k)=exp(x)i=0ck1i!xi
可以将exp(x)看成一个整体,最后的答案的形式是
∑ i = 0 w g ( i ) ∗ e x p ( x ) i = ∑ i = 0 w g ( i ) ∗ e x p ( i x ) , 其中 g ( i ) 是一个多项式 \\sum_i=0^wg(i)*exp(x)^i=\\sum_i=0^wg(i)*exp(ix),其中g(i)是一个多项式 i=0wg(i)exp(x)i=i=0wg(i)exp(ix),其中g(i)是一个多项式
g(i)最高次数不超过 ∑ c i \\sumc_i ci,而exp(ix)的每项系数都是已知,那么只要遍历每个g(i)项的系数乘上exp(ix)对应项系数即可。最后别忘了乘n!
具体写法可以借鉴dp的思路,设dpij表示前i个f相乘得到的式子exp(jx)的系数是什么,存的是个多项式
转移方程 d p i , j = d p i − 1 , j − d p i , j − 1 ∗ ∑ k = 0 c i − 1 x k k ! dp_i,j=dp_i-1,j - dp_i,j-1*\\sum_k=0^c_i-1\\fracx^kk! dpi,j=dpi1,jdpi,j1k=0ci1k!xk,其实就是模拟多项式暴力算罢了,这样思路清楚一点。
复杂度 O ( w 2 s l o g s + q w s ) , s = ∑ c i O(w^2slogs+qws),s=\\sumci O(w2slogs+qws),s=ci

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ios ios::sync_with_stdio(false),cin.tie(nullptr) 
#define _for(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define _rep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define pii pair<int ,int >
#define pb(v) push_back(v)
#define all(v) v.begin(),v.end()
#define int long long
#define inf 0x3f3f3f3f
#define INF 0x7f7f7f7f7f7f7f7f
#define endl "\\n"
#define fi first
#define se second
#define ls p<<1
#define rs p<<1|1
#define lson p<<1,l,mid
#define rson p<<1|1,mid+1,r
#define AC return 0
#define ldb long double
const int maxn = 6e6+10;
const int p = 998244353;
const int mod = 998244353;
int Pow(int x,int d)
    int tans = 1;
    if(d == 0)return 1%p;
    int a = Pow(x,d/2);
    tans = 1ll*a*a%p;
    if(d%2)tans = 1ll*tans*x%p;
    return tans%p;

typedef vector<int> Poly;//多项式定义
inline Poly  operator + (const Poly&A ,const Poly&B)
    Poly res (max(A.size(),B.size()),0);
    for(int i=0 ;i<max(A.size(),B.size()) ;i++)
        if( i < A.size() && i<B.size() ) res[i] = (A[i]+B[i])%mod;
        else if( i >= A.size() )
            res[i] = B[i];
        
        else res[i] = A[i];
    
    return res;

inline Poly  operator - (const Poly&A ,const Poly&B)
    Poly res (max(A.size(),B.size()),0);
    for(int i=0 ;i<max(A.size(),B.size()) ;i++)
        if( i < A.size() && i<B.size() ) res[i] = (A[i]-B[i]+mod)%mod;
        else if( i >= A.size() )
            res[i] = (mod-B[i])%mod;
        
        else res[i] = A[i];
    
    return res;

int F1[maxn],F2[maxn];
int rev[maxn];
void NTT(int * A,int lim,int opt) 
    int i, j, k, m, gn, g, tmp;
    for(int i = 0; i < lim; ++i)rev[i] = (i & 1)*(lim >> 1) + (rev[i >> 1] >> 1);
    for(i = 0; i < lim; ++i)if (rev[i] < i) swap(A[i], A[rev[i]]);
    for(m = 2; m <= lim; m <<= 1) 
        k = m >> 1;
        gn = Pow(3,(p - 1) / m);
        for(i = 0; i < lim; i += m) 
            g = 1;
            for (j = 0; j < k; ++j, g = 1ll * g * gn % p) 
                tmp = 1ll * A[i + j + k] * g % p;
                A[i + j + k] = (A[i + j] - tmp + p) % p;
                A[i + j] = (A[i + j] + tmp) % p;
            
        
    
    if(opt == -1)
        reverse(A+1,A+lim);
        int inv = Pow(lim,p-2);
        for(i = 0; i < lim; ++i) A[i] = 1ll * A[i] * inv % p;
    

Poly mul(const Poly & A,const Poly & B)
    int n = A.size(),m = B.size();
    int siz = n + m - 1;
    Poly C(siz);
    if(siz < 64)//小于等于64项直接暴力算
        for(int i = 0;i < n;i++)
            for(int j = 02022 牛客多校 第四场 C

2022牛客多校第一场ACDGIJ

2022牛客多校第一场ACDGIJ

2022牛客多校第一场ACDGIJ

2022牛客多校第二场CDE

2022牛客多校第二场CDE