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=0ck−1i!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=dpi−1,j−dpi,j−1∗∑k=0ci−1k!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