BZOJ3684 大朋友与多叉树(拉格朗日反演)
BZOJ3684 大朋友与多叉树(拉格朗日反演)
题目大意
我们的大朋友很喜欢计算机科学,而且尤其喜欢多叉树。对于一棵带有正整数点权的有根多叉树,如果它满足这样的性质,我们的大朋友就会将其称作神犇的:点权为1的结点是叶子结点;对于任一点权大于1的结点u,u的孩子数目deg[u]属于集合D,且u的点权等于这些孩子结点的点权之和。
给出一个整数s,你能求出根节点权值为s的神犇多叉树的个数吗?请参照样例以更好的理解什么样的两棵多叉树会被视为不同的。
我们只需要知道答案关于950009857(453*2^21+1,一个质数)取模后的值。
数据范围
1<=m<s<=10^5, 2<=d[i]<=s
解题思路
拉格朗日反演经典题
写出递推式
\[T(x) = x+\sum_{i\in S}T(x)^i
\]
使用拉格朗日反演
\[x = T(x)-\sum_{i \in S}T(x)^i
\]
设 \(G(x)=T(x)\),\(F(x)=x-\sum_{i\in S}x^i\)
有 \(F(G(X)) = x\),所以可以直接求出 \([x^n]G(x)=\frac 1n[x^{n-1}]{\left(\frac{x}{F(x)}\right)}^n\)
代码
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template<typename F>
inline void write(F x, char ed = '\n')
{
static short st[30];short tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
putchar(ed);
}
template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }
template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }
const int N = 333400;
const int P = 950009857;
int r[N], lim, n;
ll A[N], B[N], C[N], D[N], E[N], f[N], g[N];
ll inv[N];
void init(void) {
inv[0] = inv[1] = 1;
for (int i = 2;i <= 2 * n; i++)
inv[i] = inv[P % i] * (P - P / i) % P;
}
ll fpw(ll x, ll mi) {
ll res = 1;
for (; mi; mi >>= 1, x = x * x % P)
if (mi & 1) res = res * x % P;
return res;
}
void NTT(ll *a, int ty) {
for (int i = 0;i < lim; i++)
if (r[i] > i) swap(a[i], a[r[i]]);
for (int i = 1;i < lim; i <<= 1) {
ll wn = fpw(7, (P - 1) / (i << 1));
for (int j = 0;j < lim; j += (i << 1)) {
ll w = 1;
for (int k = 0;k < i; k++, w = w * wn % P) {
ll x = a[j+k], y = a[i+j+k] * w % P;
a[j+k] = (x + y) % P, a[i+j+k] = (x - y + P) % P;
}
}
}
if (ty == -1) {
ll inv = fpw(lim, P - 2);
for (int i = 0;i < lim; i++)
a[i] = a[i] * inv % P;
reverse(a + 1, a + lim);
}
}
/*
F(G) - A = 0;
F(G0) - A = 0;
0 = T(G0)+T(G0)'(G-G0)
G = G0-T(G0)/T(G0)'
1/G0 - A = 0;
G = G0 + (1/G0-A)G0^2
G = G0 + G0 - G0^2A
G = G0(2 - A*G0)
*/
void Inv(ll *a, ll *b, int deg) {
b[0] = fpw(a[0], P - 2); int len;
for (len = 2;len < (deg << 1); len <<= 1) {
lim = len << 1;
for (int i = 1;i < lim; i++)
r[i] = (r[i>>1]>>1) | ((i & 1) ? len : 0);
for (int i = 0;i < len; i++) A[i] = a[i], B[i] = b[i];
NTT(A, 1), NTT(B, 1);
for (int i = 0;i < lim; i++)
b[i] = (2 + (P - A[i]) * B[i]) % P * B[i] % P;
NTT(b, -1);
for (int i = len;i < lim; i++) b[i] = 0;
}
for (int i = 0;i < len; i++) A[i] = B[i] = 0;
}
void chick(ll *a, int deg) {
for (int i = deg - 1;i >= 1; i--)
a[i] = a[i-1] * inv[i] % P;
a[0] = 0;
}
void door(ll *a, int deg) {
for (int i = 1;i < deg; i++)
a[i-1] = a[i] * i % P;
a[deg - 1] = 0;
}
void Ln(ll *a, int deg) {
Inv(a, C, deg), door(a, deg);
NTT(a, 1), NTT(C, 1);
for (int i = 0;i < lim; i++)
a[i] = a[i] * C[i] % P;
NTT(a, -1), chick(a, deg);
}
/*
F(G) - A = 0;
F(G0) - A = 0;
0 = T(G0)+T(G0)'(G-G0)
G = G0-T(G0)/T(G0)'
1/G0 - A = 0;
G = G0 + (LnG0-A)G0
G = -(-1+LnG0-A)G0
*/
void Exp(ll *a, ll *b, int deg) {
b[0] = 1; int len;
for (len = 2;len < (deg << 1); len <<= 1) {
lim = len << 1;
for (int i = 0;i < len; i++) D[i] = b[i];
Ln(D, len); D[0] = (a[0] + 1 - D[0] + P) % P;
for (int i = 1;i < len; i++)
D[i] = (a[i] - D[i] + P) % P;
NTT(D, 1), NTT(b, 1);
for (int i = 0;i < lim; i++)
b[i] = b[i] * D[i] % P;
NTT(b, -1);
for (int i = len;i < lim; i++) b[i] = 0;
}
}
ll G[N];
void lage() {
Inv(f, E, n), Ln(E, n);
for (int i = 0;i < n; i++)
E[i] = E[i] * n % P;
Exp(E, G, n);
}
int m;
int main() {
read(n), read(m), init(); f[0] = 1;
for (int i = 0, t;i < m; i++) read(t), f[t-1] += P - 1;
lage();
write(G[n-1] * inv[n] % P);
return 0;
}