[BZOJ 5093][Lydsy1711月赛]图的价值
统计的时候,考虑每个店对每个图的贡献,可以看出答案是:
\[n2^{\tfrac{(n - 1)(n - 2)}{2}}\,\sum_{i = 0}^{n - 1} \binom{n - 1}{i} i^k\]
求和号前面还好说,主要看求和号后面的咋搞。
后面的那一部分是个经典题吧……但我还是来推导一番吧:
\[
\begin{aligned}
\sum_{i = 0}^{m} \binom{m}{i} i^k&= \sum_{i = 0}^{m} \binom{m}{i}\sum_{j = 0}^k S(k, j) j!\binom{i}{j}\\
&= \sum_{j = 0}^k S(k, j) j! \sum_{i = 0}^m \binom{m}{i}\binom{i}{j} \\
&= \sum_{j = 0}^k S(k, j) m^{\underline{j}} \sum_{i = 0}^{m} \binom{n - j}{i - j} \\
&= \sum_{j = 0}^k S(k, j) m^{\underline{j}} 2^{m - j}
\end{aligned}
\]
然后如果我们能高效的求出某一行的第二类斯特林数,那么问题就迎刃而解了。
考虑斯特林反演(这本质上是一个容斥,用二项式反演也可以推导):
\[S(k, j) = \sum_{i = 0}^j \frac{(-1)^{j - i}}{(j - i)!} \cdot \frac{i^k}{i!}\]
这个东西很显然是一个卷积……而且模数还很友好(UOJ模数),用NTT求出一行的第二类斯特林数即可。
代码:
/************************************************************** Problem: 5093 User: danihao123 Language: C++ Result: Accepted Time:22632 ms Memory:25824 kb ****************************************************************/ #include <cstdio> #include <cstring> #include <cstdlib> #include <cctype> #include <algorithm> #include <utility> typedef long long ll; const ll ha = 998244353LL; const ll bs = 3LL; inline ll pow_mod(const ll &a, ll b) { ll ans = 1LL, res = a; while(b) { if(1LL & b) ans = (ans * res) % ha; res = (res * res) % ha; b >>= 1; } return ans; } inline ll inv(const ll &x) { return pow_mod(x, ha - 2LL); } inline int flip(const int &bi, const int &x) { int ans = 0; for(int i = 0; i < bi; i ++) { if((1 << i) & x) { ans |= (1 << (bi - i - 1)); } } return ans; } inline void ntt(ll *A, const int &bi, const int &len, bool flag = false) { for(int i = 0; i < len; i ++) { int v = flip(bi, i); if(v < i) std::swap(A[v], A[i]); } for(int L = 1; L < len; L <<= 1) { ll xi = pow_mod(bs, (ha - 1LL) / (ll(L << 1))); if(flag) xi = inv(xi); for(int i = 0; i < len; i += (L << 1)) { ll w = 1LL; for(int j = i; j < i + L; j ++) { ll x = A[j], y = A[j + L]; A[j] = (x + (w * y) % ha) % ha; A[j + L] = (x - (w * y) % ha + ha) % ha; w = (w * xi) % ha; } } } } const int maxn = 800005; ll A[maxn], B[maxn]; ll fac[maxn], down[maxn]; int main() { ll n; int k; scanf("%lld%d", &n, &k); if(n == 1) { puts("0"); return 0; } int len = 1, bi = 0; while(len <= (2 * k)) { len <<= 1; bi ++; } fac[0] = 1LL; for(int i = 1; i <= k; i ++) { fac[i] = (fac[i - 1] * (ll(i))) % ha; } for(int i = 0; i <= k; i ++) { A[i] = (inv(fac[i]) * pow_mod(ha - 1LL, i)) % ha; } for(int i = 0; i <= k; i ++) { B[i] = inv(fac[i]); B[i] = (B[i] * pow_mod(i, k)) % ha; } ntt(A, bi, len); ntt(B, bi, len); for(int i = 0; i < len; i ++) { A[i] = (A[i] * B[i]) % ha; } ntt(A, bi, len, true); ll inv_l = inv(len); for(int i = 0; i < len; i ++) { A[i] = (A[i] * inv_l) % ha; } #ifdef LOCAL for(int i = 0; i <= k; i ++) { printf("%lld ", A[i]); } puts(""); #endif down[0] = 1LL; for(int i = 1; i <= std::min(ll(k), n - 1LL); i ++) { down[i] = (down[i - 1] * (n - (ll(i)))) % ha; } ll ans = 0LL; for(int i = 1; i <= std::min(ll(k), n - 1LL); i ++) { ll delta = (A[i] * down[i]) % ha; delta = (delta * pow_mod(2, n - 1LL - (ll(i)))) % ha; ans = (ans + delta) % ha; } ans = (ans * pow_mod(2LL, (n - 1LL) * (n - 2LL) / 2LL)) % ha; ans = (ans * n) % ha; printf("%lld\n", ans); return 0; }