# [BZOJ3684]大朋友和多叉树

### [BZOJ3684]大朋友和多叉树

#### 输入示例

4 2
2 3


#### 输出示例

10


#### 数据规模及约定

$$1 \le m < s \le 10^5, 2 \le d[i] \le s$$，有 $$3$$ 组小数据和 $$3$$ 组大数据。

#### 题解

$f(i) = \sum_{j \in D} g(i, j) \\ f(1) = 1 \\ g(i, j) = \sum_{k = 1}^j g(i - k, j - 1) \cdot f(k)$

F(x) = x + \sum_{j \in D} F(x)^j

$$f(x)$$$$g(x)$$ 常数项为 $$0$$，且 $$g(f(x)) = x$$，那么若已知 $$g(x)$$，我们可以快速求 $$f(x)$$某一项（不妨设要求的是 $$x^n$$ 项），我们有：

$[x^n]f(x) = \frac{1}{n} [x^{-1}] \left( \frac{1}{g(x)} \right) ^n$

$[x^n]f(x) = \frac{1}{n} [x^{dn-1}] \left( \frac{x^d}{g(x)} \right) ^n$

$F(x) - \sum_{j \in D} F(x)^j = x \\ G(F(x)) = x$

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define rep(i, s, t) for(int i = (s), mi = (t); i <= mi; i++)
#define dwn(i, s, t) for(int i = (s), mi = (t); i >= mi; i--)

int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}

#define maxn 262144
#define MOD 950009857
#define Groot 7
#define LL long long

int Pow(int a, int b) {
int ans = 1, t = a;
while(b) {
if(b & 1) ans = (LL)ans * t % MOD;
t = (LL)t * t % MOD; b >>= 1;
}
return ans;
}

int inv[maxn];
void init() {
inv[1] = 1;
rep(i, 2, maxn - 1) inv[i] = (LL)(MOD - MOD / i) * inv[MOD%i] % MOD;
return ;
}

int brev[maxn];
void FFT(int *a, int len, int tp) {
int n = 1 << len;
rep(i, 0, n - 1) if(i < brev[i]) swap(a[i], a[brev[i]]);
rep(i, 1, len) {
int wn = Pow(Groot, MOD - 1 >> i);
if(tp < 0) wn = Pow(wn, MOD - 2);
for(int j = 0; j < n; j += 1 << i) {
int w = 1;
rep(k, 0, (1 << i >> 1) - 1) {
int la = a[j+k], ra = (LL)w * a[j+k+(1<<i>>1)] % MOD;
a[j+k] = (la + ra) % MOD;
a[j+k+(1<<i>>1)] = (la - ra + MOD) % MOD;
w = (LL)w * wn % MOD;
}
}
}
if(tp < 0) {
int invn = Pow(n, MOD - 2);
rep(i, 0, n - 1) a[i] = (LL)a[i] * invn % MOD;
}
return ;
}

void Mul(int *A, int *B, int n, int m, bool recover = 0) {
int N = 1, len = 0;
while(N <= n + m) N <<= 1, len++;
rep(i, 0, N - 1) brev[i] = (brev[i>>1] >> 1) | ((i & 1) << len >> 1);
rep(i, n + 1, N - 1) A[i] = 0;
rep(i, m + 1, N - 1) B[i] = 0;
FFT(A, len, 1); FFT(B, len, 1);
rep(i, 0, N - 1) A[i] = (LL)A[i] * B[i] % MOD;
FFT(A, len, -1); if(recover) FFT(B, len, -1);
return ;
}

int tmp[maxn];
void inverse(int *f, int *g, int n) {
if(n == 1) return (void)(f[0] = Pow(g[0], MOD - 2));
inverse(f, g, n + 1 >> 1);
rep(i, 0, n - 1) tmp[i] = g[i];
int N = 1, len = 0;
while(N < (n << 1)) N <<= 1, len++;
rep(i, 0, N - 1) brev[i] = (brev[i>>1] >> 1) | ((i & 1) << len >> 1);
rep(i, n, N - 1) tmp[i] = 0;
rep(i, n + 1 >> 1, N - 1) f[i] = 0;
FFT(f, len, 1); FFT(tmp, len, 1);
rep(i, 0, N - 1) f[i] = ((LL)f[i] * (2ll - (LL)tmp[i] * f[i] % MOD) % MOD + MOD) % MOD;
FFT(f, len, -1);
return ;
}

int deri[maxn], pinv[maxn];
void logarithm(int *f, int *g, int n) { // g[0] must be 1
rep(i, 1, n - 1) deri[i-1] = (LL)g[i] * i % MOD;
inverse(pinv, g, n);
Mul(deri, pinv, n - 2, n - 1);
rep(i, 0, n - 2) f[i+1] = (LL)deri[i] * inv[i+1] % MOD; f[0] = 0;
return ;
}

int lnf[maxn], tg[maxn];
void exponential(int *f, int *g, int n) { // g[0] must be 0
if(n == 1) return (void)(f[0] = 1);
exponential(f, g, n + 1 >> 1);
rep(i, n + 1 >> 1, n - 1) f[i] = 0;
logarithm(lnf, f, n);
rep(i, 0, n - 1) tg[i] = (g[i] - lnf[i] + MOD) % MOD; tg[0]++; if(tg[0] >= MOD) tg[0] -= MOD;
Mul(f, tg, (n + 1 >> 1) - 1, n - 1);
return ;
}

int t1[maxn];
void p_pow(int *a, int n, int b) { // a[0] must be 1
logarithm(t1, a, n);
rep(i, 0, n - 1) t1[i] = (LL)t1[i] * b % MOD;
exponential(a, t1, n);
return ;
}

int n, m, d[maxn], ans[maxn];
int main() {
init();

rep(i, 1, m) d[read()-1] = MOD - 1;
d[0] = 1;
p_pow(d, n, n);
inverse(ans, d, n);

printf("%lld\n", (LL)inv[n] * ans[n-1] % MOD);

return 0;
}


$$[1 \sim 3]$$：拉格朗日反演定理的证明，为什么 $$f(g(x)) = g(f(x)) = x$$ 以及所谓的“分式域”：本文为尽量通俗易懂地阐述此题解法并没有严格地用数学符号，也没有给出一些结论的证明，详见这里

posted @ 2018-02-20 16:37  xjr01  阅读(1110)  评论(0编辑  收藏  举报