CF1709F Multiset of Strings 题解
一道比较简单的多项式计数题(这也能算计数?)但是场上 sb 了没看出来应该放在 01-trie 上做。
题意略。
把这个题目放在一个插入了所有
蓝色的字母表示边权
问题就转化为了,从根节点(第
对于每个节点
首先你可以发现同一层的点的 dp 值肯定是全部相同的,也就是我们只需要对
具体来说用
初始化第
从第
所以有转移方程:
最后一点细节是第一层的节点
用 NTT 进行多项式操作,时间复杂度是
代码如下:
#include <bits/stdc++.h>
const int N = 524288, mod = 998244353, g = 3;
inline int mul(int x, int y){
return (int)(1ll * x * y % (1ll * mod));
}
inline int add(int x, int y){
return x + y >= mod ? x + y - mod : x + y;
}
inline int minus(int x, int y){
return x < y ? x - y + mod : x - y;
}
inline int Qpow(int x, int y){
int r = 1;
while(y){
if(y & 1) r = mul(r, x);
x = mul(x, x);
y >>= 1;
}
return r;
}
int rev[N];
void NTT(int *A, int limit, int on){
for(int i = 1; i < limit; ++i)
rev[i] = (rev[i >> 1] >> 1) + (i & 1) * (limit >> 1);
for(int i = 0; i < limit; ++i)
if(i < rev[i]) std::swap(A[i], A[rev[i]]);
for(int i = 2; i <= limit; i <<= 1){
int t = Qpow(g, (mod - 1) / i);
if(on == -1) t = Qpow(t, mod - 2);
for(int j = 0; j < limit; j += i){
int w = 1;
for(int k = j; k < j + i / 2; ++k, w = mul(w, t)){
int u = A[k], v = mul(A[k + i / 2], w);
A[k] = add(u, v);
A[k + i / 2] = minus(u, v);
}
}
}
if(on == -1){
int u = Qpow(limit, mod - 2);
for(int i = 0; i < limit; ++i) A[i] = mul(A[i], u);
}
return ;
}
void work1(int *A, int limit){
NTT(A, limit, 1);
for(int i = 0; i < limit; ++i) A[i] = mul(A[i], A[i]);
NTT(A, limit, -1);
return ;
}
int n, k, f, dp[N];
signed main(){
scanf("%d%d%d", &n, &k, &f);
int limit = 1;
while(limit < 2 * k + 1) limit <<= 1;
for(int i = 0; i <= k; ++i) dp[i] = 1;
for(int i = 1; i <= n; ++i){
work1(dp, limit);
int s = 0;
for(int j = 2 * k + 1; j < limit; ++j) dp[j] = 0;
if(i == n) break;
for(int j = 2 * k; j >= 0; --j){
int tmp = dp[j];
if(j <= k)
dp[j] = add(s, mul(dp[j], std::max(0, k - j + 1)));
else dp[j] = 0;
s = add(s, tmp);
}
}
printf("%d\n", dp[f]);
return 0;
}