[洛谷 P4389]付公主的背包
付公主有一个大小为\(10^5\)的背包。然你有\(n\)种类型的物品,其中第\(i\)种体积为\(V_i\)(是正整数),数量有\(10^5\)件。然后给出一正整数\(m\),对任意\(i=1\ldots m\)求出包里恰好装了\(i\)体积的物品的方案数,答案对\(998244353\)取模。
\(1\leq n\leq 10^5, m\leq 10^5, V_i\leq m\)。
[LibreOJ 2058][TJOI2016]求和
求\(\sum_{i = 0}^n\sum_{j = 0}^i \begin{Bmatrix}i\\ j\end{Bmatrix}2^jj!\)。
\(1\leq n\leq 10^5\)。
[UOJ 50][UR #3]链式反应
给你一个集合\(S\)(保证其中元素都为小于\(n\)的自然数),定义一棵合法的树为一棵编号满足堆的性质,且非叶子节点都一定有两个可能非叶子的儿子,同时有\(c(c\in S)\)个一定为叶子的儿子。对于所有\(i = 1\ldots n\),求有多少大小为\(i\)的形态不同的合法的树。
\(n\leq 200000\)。
[LibreOJ 6268]分拆数
定义分拆数\(f(x)\)表示将\(x\)拆为若干正整数的本质不同方案数。对于\(i = 1\ldots n\),输出\(f(i)\)。
\(1\leq n\leq 10^5\)。
[CF 438E]The Child and Binary Tree
给你一个大小为\(n\)的集合\({c_1, c_2,\ldots ,c_n}\),规定合法的二叉树为带点权且点权都属于给定集合中的点的数。对于任意整数$i\in [1, m]$,求出有多少不同的点权和为\(i\)的二叉树并输出之。
\(1\leq n, m, c_i\leq 10^5\)。
[BZOJ 3456]城市规划
aji多项式求逆毁我青春,,,
设\(f_i\)表示\(i\)个点的有标号无向联通图,考虑所有可能的图(记\(F_i\)为\(i\)个点的有标号无向图,显然\(F_i = 2^{\binom{i}{2}}\))和\(f\)的关系(使用图计数的经典套路:枚举1所在的联通块大小):
\[F_n = \sum_{i = 1}^n \binom{n-1}{i-1} f_i F_{n - i}\]
看起来事卷积?但是这个卷积没有办法直接用FFT/NTT求(当然分离一下项啊,移下项就可以分治NTT力)。
考虑进一步化简柿子。完全展开后会发现右边有个非常碍眼的\((n-1)!\),所以两边除一下:
\[\frac{F_n}{(n-1)!} =\sum_{i = 1}^n \frac{f_i}{(i-1)!}\cdot \frac{F_{n - i}}{(n - i)!}\]
然后这个问题就很毒瘤了:我们要求的答案多项式(除上那个阶乘)和一个已知多项式做卷积,可以得到另一个已知多项式……这样就需要多项式除法了,于是乎多项式逆元派上了用场。
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <utility>
typedef long long ll;
const int maxn = 1020010;
const ll ha = 1004535809LL;
const ll bs = 3LL;
ll pow_mod(ll a, ll b) {
ll ans = 1LL, res = a % ha;
while(b) {
if(1LL & b) ans = (ans * res) % ha;
res = (res * res) % ha;
b >>= 1;
}
return ans;
}
ll inv(ll x) {
return pow_mod(x, ha - 2LL);
}
int flip(int bi, int x) {
int ans = 0;
for(int i = 0; i < bi; i ++) {
if((1 << i) & x) {
ans += (1 << (bi - i - 1));
}
}
return ans;
}
void NTT(ll *A, int bi, bool flag = false) {
int n = 1 << bi;
for(int i = 0; i < n; i ++) {
int v = flip(bi, i);
if(v < i) std::swap(A[v], A[i]);
}
for(int L = 1; L < n; L <<= 1) {
ll xi = pow_mod(3LL, (ha - 1LL) / (ll(L << 1)));
if(flag) xi = inv(xi);
for(int i = 0; i < n; i += (L << 1)) {
ll w = 1LL;
for(int j = i; j < i + L; j ++) {
ll v1 = A[j], v2 = A[j + L];
A[j] = (v1 + (w * v2) % ha) % ha;
A[j + L] = (v1 - (w * v2) % ha + ha) % ha;
w = (w * xi) % ha;
}
}
}
}
void poly_mul(ll *A, ll *B, int bi, ll *C) {
static ll T1[maxn], T2[maxn];
int n = (1 << bi);
std::copy(A, A + n, T1);
std::copy(B, B + n, T2);
NTT(T1, bi); NTT(T2, bi);
#ifdef LOCAL
puts("poly_mul :");
for(int i = 0; i < (n); i ++) {
printf("%lld ", A[i]);
}
puts("");
for(int i = 0; i < (n); i ++) {
printf("%lld ", B[i]);
}
puts("");
for(int i = 0; i < (n); i ++) {
printf("%lld ", T1[i]);
}
puts("");
for(int i = 0; i < (n); i ++) {
printf("%lld ", T2[i]);
}
puts("");
#endif
for(int i = 0; i < n; i ++) {
T1[i] = (T1[i] * T2[i]) % ha;
}
#ifdef LOCAL
for(int i = 0; i < (n); i ++) {
printf("%lld ", T1[i]);
}
puts("");
#endif
NTT(T1, bi, true);
ll inv_n = inv(n);
for(int i = 0; i < n; i ++) {
T1[i] = (T1[i] * inv_n) % ha;
}
std::copy(T1, T1 + n, C);
#ifdef LOCAL
for(int i = 0; i < (n); i ++) {
printf("%lld ", C[i]);
}
puts("");
#endif
}
void poly_inv(int mod, ll *B, ll *BB) {
if(mod == 1) {
BB[0] = inv(B[0]);
} else {
poly_inv((mod + 1) >> 1, B, BB);
int bi = 0, sz = 1;
while(sz <= ((mod * 2) + 1)) {
bi ++; sz <<= 1;
}
ll inv_sz = inv(sz);
static ll tmp[maxn];
std::copy(B, B + mod, tmp);
std::fill(tmp + mod, tmp + sz, 0LL);
NTT(tmp, bi); NTT(BB, bi);
for(int i = 0; i < sz; i ++) {
tmp[i] = (tmp[i] * BB[i]) % ha;
tmp[i] = (tmp[i] * (ha - 1LL)) % ha;
tmp[i] = (tmp[i] + 2LL) % ha;
tmp[i] = (tmp[i] * BB[i]) % ha;
}
NTT(tmp, bi, true);
for(int i = 0; i < sz; i ++) {
tmp[i] = (tmp[i] * inv_sz) % ha;
}
std::copy(tmp, tmp + mod, BB);
std::fill(BB + mod, BB + sz, 0LL);
}
}
int main() {
static ll fac[maxn], A[maxn], B[maxn], BB[maxn];
int n; scanf("%d", &n);
int bi = 0, sz = 1;
while(sz <= n + 1) {
bi ++; sz <<= 1;
}
fac[0] = 1LL;
for(int i = 1; i <= n; i ++) {
fac[i] = (fac[i - 1] * (ll(i))) % ha;
}
B[0] = 1LL;
for(int i = 1; i <= n; i ++) {
B[i] = pow_mod(2LL, (ll(i)) * (ll(i - 1)) / 2LL);
B[i] = (B[i] * inv(fac[i])) % ha;
}
for(int i = 1; i <= n; i ++) {
A[i] = pow_mod(2LL, (ll(i)) * (ll(i - 1)) / 2LL);
A[i] = (A[i] * inv(fac[i - 1])) % ha;
}
poly_inv(n + 1, B, BB);
poly_mul(A, BB, bi, A);
printf("%lld\n", (A[n] * fac[n - 1]) % ha);
return 0;
}
[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;
}
[BZOJ 3992][SDOI2015]序列统计
终于调过去了……(然而不就是道NTT+生成函数水题吗至于调半天)
首先积非常的恶心,考虑转成和。这个事需要求指标的……求指标的话枚举原根的若干次幂即可(恰好$m$是素数一定有原根……),判断原根用比较大力的手段即可(我搞了一个$O(n\sqrt{n}logn)$的……求轻喷)。
然后这题还算比较简单吧……用生成函数表示原来的集合,然后$n$次幂就可以了。
注意那事个循环卷积……所以要开两倍然后每次乘完了再把右半部分搞过去。
代码:
/**************************************************************
Problem: 3992
User: danihao123
Language: C++
Result: Accepted
Time:6652 ms
Memory:1864 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <queue>
#include <utility>
#include <vector>
#include <cmath>
typedef long long ll;
const int maxn = 32005;
const ll ha = 1004535809LL;
const ll bs = 3LL;
ll n, m, gl; int sz;
ll pow_mod(ll a, ll b, ll p) {
if(!b) return 1LL;
ll ans = pow_mod(a, b >> 1, p);
ans = (ans * ans) % p;
if(b & 1LL) ans = (ans * a) % p;
return ans;
}
ll inv(ll x, ll p) {
return pow_mod(x, p - 2LL, p);
}
void break_up(ll x, std::vector<ll> &V) {
int bd = sqrt(x + 0.5);
for(ll i = 2; i <= bd; i ++) {
if(x % i == 0) {
V.push_back(i);
while(x % i == 0) x /= i;
}
if(x == 1LL) break;
}
if(x > 1LL) V.push_back(x);
}
ll get_phi() {
if(m == 2LL) return 1LL;
ll mi = m - 1LL;
std::vector<ll> V;
break_up(mi, V);
for(ll i = 2; i <= mi; i ++) {
bool ok = true;
for(int j = 0; j < V.size(); j ++) {
ll b = mi / V[j];
if(pow_mod(i, b, m) == 1LL) {
ok = false;
#ifdef LOCAL
printf("%lld not passed!\n", i);
#endif
break;
}
}
if(ok) {
return i;
}
}
}
int bi, len;
int flip(int x) {
int ans = 0;
for(int i = 0; i < bi; i ++) {
if((1 << i) & x) {
ans += (1 << (bi - i - 1));
}
}
return ans;
}
void ntt(ll *A, bool flag = false) {
for(int i = 0; i < len; i ++) {
int v = flip(i);
if(v < i) std::swap(A[i], A[v]);
}
for(int L = 1; L < len; L <<= 1) {
ll xi_n = pow_mod(3LL, (ha - 1LL) / (ll(L << 1)), ha);
if(flag) xi_n = inv(xi_n, ha);
for(int i = 0; i < len; i += (L << 1)) {
ll w = 1;
for(int j = i; j < i + L; j ++) {
ll p1 = A[j], p2 = A[j + L];
A[j] = (p1 + (p2 * w) % ha) % ha;
A[j + L] = (p1 - (p2 * w) % ha + ha) % ha;
w = (w * xi_n) % ha;
}
}
}
}
void poly_mul(ll *A, ll *B) {
static ll C[maxn]; memset(C, 0, sizeof(C));
#ifdef LOCAL
printf("A :");
for(int i = 0; i < len; i ++) printf(" %lld", A[i]);
puts("");
printf("B :");
for(int i = 0; i < len; i ++) printf(" %lld", B[i]);
puts("");
#endif
ntt(A); ntt(B);
for(int i = 0; i < len; i ++) C[i] = (A[i] * B[i]) % ha;
ntt(C, true);
ll inv_n = inv(len, ha);
for(int i = 0; i < len; i ++) {
C[i] = (C[i] * inv_n) % ha;
}
#ifdef LOCAL
printf("C (not processed) :");
for(int i = 0; i < len; i ++) printf(" %lld", C[i]);
puts("");
#endif
for(int i = 0; i < len; i ++) {
int v = i % (m - 1LL);
if(v != i) {
C[v] = (C[v] + C[i]) % ha;
C[i] = 0LL;
}
}
#ifdef LOCAL
printf("C :");
for(int i = 0; i < len; i ++) printf(" %lld", C[i]);
puts("");
#endif
std::copy(C, C + len, A);
}
void poly_pow(ll *A, ll b) {
static ll B[maxn];
static ll res[maxn];
std::copy(A, A + len, res);
std::fill(A, A + len, 0); A[0] = 1LL;
#ifdef LOCAL
printf("A :");
for(int i = 0; i < len; i ++) printf(" %lld", A[i]);
puts("");
printf("res : ");
for(int i = 0; i < len; i ++) printf("%lld ", res[i]);
puts("");
#endif
while(b) {
if(1LL & b) {
std::copy(res, res + len, B);
poly_mul(A, B);
std::fill(B, B + len, 0);
}
std::copy(res, res + len, B);
poly_mul(res, B);
std::fill(B, B + len, 0);
b >>= 1LL;
}
}
int main() {
static bool vis[maxn];
static ll A[maxn];
scanf("%lld%lld%lld%d", &n, &m, &gl, &sz);
ll phi = get_phi();
for(int i = 1; i <= sz; i ++) {
int v; scanf("%d", &v);
if(v == 0) continue;
vis[v] = true;
}
int logx = 0;
bi = 0; len = 1;
while(len <= (2 * m - 2)) {
bi ++; len <<= 1;
}
for(int i = 0; i < (m - 1); i ++) {
int v = pow_mod(phi, i, m);
if(vis[v]) {
A[i] ++;
#ifdef LOCAL
printf("log(%d) : %d\n", v, i);
#endif
}
if(v == gl) {
logx = i;
}
}
poly_pow(A, n);
printf("%lld\n", A[logx]);
return 0;
}