[多项式ln][多项式exp][多项式求幂][生成函数][DP][FNT] BZOJ 3684: 大朋友和多叉树
2018-03-06 13:00
399 查看
SolutionSolution
把DP写成生成函数的形式。f(x)=x+∑d∈Dfd(x)f(x)=x+∑d∈Dfd(x)设g(f(x))=xg(f(x))=x,有g(f(x))g(x)==f(x)−∑d∈Dfd(x)x−∑d∈Dxdg(f(x))=f(x)−∑d∈Dfd(x)g(x)=x−∑d∈Dxd根据拉格朗日反演[xn]h(f(x))=1n[ωn−1]h′(ω)(ωg(ω))n[xn]h(f(x))=1n[ωn−1]h′(ω)(ωg(ω))n令h(x)=xh(x)=x就得到了[xn]f(x)=1n[ωn−1](ωg(ω))n[xn]f(x)=1n[ωn−1](ωg(ω))n现在要求的就是(g(ω)ω)−n(g(ω)ω)−n这个东西。fk(x)=eklnf(x)fk(x)=eklnf(x)lnln直接多项式求逆,expexp牛顿迭代。O(nlogn)O(nlogn)。#include <bits/stdc++.h> #define show(x) cerr << #x << " = " << x << endl using namespace std; typedef long long ll; typedef pair<int, int> pairs; const int N = 303030; const int MOD = 950009857; inline char get(void) { static char buf[100000], *S = buf, *T = buf; if (S == T) { T = (S = buf) + fread(buf, 1, 100000, stdin); if (S == T) return EOF; } return *S++; } template<typename T> inline void read(T &x) { static char c; x = 0; int sgn = 0; for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1; for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0'; if (sgn) x = -x; } inline int pwr(int a, int b) { int c = 1; while (b) { if (b & 1) c = (ll)c * a % MOD; b >>= 1; a = (ll)a * a % MOD; } return c; } inline int ivs(int x) { return pwr(x, MOD - 2); } inline int sum(int a, int b) { a += b; return a >= MOD ? a - MOD : a; } inline int sub(int a, int b) { return a < b ? a - b + MOD : a - b; } inline void add(int &x, int a) { x = sum(x, a); } namespace FNT { const int MAXN = 303030; int ww[MAXN], iw[MAXN]; int rev[MAXN]; int num; inline void pre(int n) { num = n; int g = pwr(7, (MOD - 1) / n); ww[0] = iw[0] = 1; for (int i = 1; i < num; i++) iw[n - i] = ww[i] = (ll)ww[i - 1] * g % MOD; } inline void fnt(int *a, int n, int f) { static int x, y, *w; w = (f == 1) ? ww : iw; for (int i = 0; i < n; i++) if (rev[i] > i) swap(a[rev[i]], a[i]); for (int i = 1; i < n; i <<= 1) for (int j = 0; j < n; j += (i << 1)) for (int k = 0; k < i; k++) { x = a[j + k]; y = (ll)a[j + k + i] * w[num / (i << 1) * k] % MOD; a[j + k] = sum(x, y); a[j + k + i] = sub(x, y); } if (f == -1){ int in = ivs(n); for (int i = 0; i < n; i++) a[i] = (ll)a[i] * in % MOD; } } } int inv ; inline void pre(int n) { inv[1] = 1; for (int i = 2; i <= n; i++) inv[i] = (ll)(MOD - MOD / i) * inv[MOD % i] % MOD; } void getInv(int *a, int *b, int n) { using namespace FNT; static int tmp ; if (n == 1) return (void)(b[0] = ivs(a[0])); getInv(a, b, n >> 1); for (int i = 0; i < n; i++) { tmp[i] = a[i]; tmp[i + n] = 0; } int L = 0; while (!(n >> L & 1)) L++; for (int i = 0; i < (n << 1); i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << L); fnt(tmp, n << 1, 1); fnt(b, n << 1, 1); for (int i = 0; i < (n << 1); i++) tmp[i] = (ll)b[i] * sub(2, (ll)tmp[i] * b[i] % MOD) % MOD; fnt(tmp, n << 1, -1); for (int i = 0; i < n; i++) { b[i] = tmp[i]; b[n + i] = 0; } } inline void getLn(int *a, int *b, int n) { using namespace FNT; static int da , ia , tmp ; for (int i = 0; i < (n << 1); i++) tmp[i] = da[i] = ia[i] = 0; getInv(a, ia, n); for (int i = 1; i < n; i++) da[i - 1] = (ll)a[i] * i % MOD; int L = 0; while (!(n >> L & 1)) L++; for (int i = 0; i < (n << 1); i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << L); fnt(da, n << 1, 1); fnt(ia, n << 1, 1); for (int i = 0; i < (n << 1); i++) tmp[i] = (ll)da[i] * ia[i] % MOD; fnt(tmp, n << 1, -1); b[0] = b = 0; for (int i = 1; i < n; i++) { b[i] = (ll)tmp[i - 1] * inv[i] % MOD; b[n + i] = 0; } } inline void getExp(int *a, int *b, int n) { using namespace FNT; static int lb ; if (n == 1) return (void)(b[0] = 1); getExp(a, b, n >> 1); for (int i = 0; i < (n << 1); i++) lb[i] = 0; getLn(b, lb, n); for (int i = 0; i < n; i++) lb[i] = sub(a[i], lb[i]); lb[0] = sum(lb[0], 1); int L = 0; while (!(n >> L & 1)) L++; for (int i = 0; i < (n << 1); i++) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << L); fnt(b, n << 1, 1); fnt(lb, n << 1, 1); for (int i = 0; i < (n << 1); i++) b[i] = (ll)b[i] * lb[i] % MOD; fnt(b, n << 1, -1); for (int i = 0; i < n; i++) b[i + n] = 0; } inline void getPow(int *a, int *b, int n, int k) { static int la ; k = (k % MOD + MOD) % MOD; getLn(a, la, n); for (int i = 0; i < n; i++) la[i] = (ll)la[i] * k % MOD; getExp(la, b, n); } int n, m, x, l; int g , f , g_n ; int main(void) { freopen("1.in", "r", stdin); freopen("1.out", "w", stdout); FNT::pre(1 << 18); pre(1 << 18); read(n); read(m); for (l = 1; l <= n; l <<= 1); for (int i = 0; i < m; i++) { read(x); g[x - 1] = MOD - 1; } g[0] = 1; getPow(g, g_n, l, -n); cout << (ll)g_n[n - 1] * inv % MOD << endl; return 0; }
相关文章推荐
- BZOJ 3684 大朋友与多叉树 多项式求幂/求exp+拉格朗日反演
- bzoj 3684: 大朋友和多叉树 生成函数
- [多项式ln][多项式exp][背包DP][生成函数] LOJ #556. 咱们去烧菜吧
- BZOJ 3684: 大朋友和多叉树 [拉格朗日反演 多项式k次幂 生成函数]
- BZOJ 3684 大朋友和多叉树(生成函数+FFT)
- BZOJ 3684 大朋友和多叉树 FFT+拉格朗日反演
- BZOJ 3456: 城市规划 [多项式求逆元 组合数学 | 生成函数 多项式求ln]
- 【XSY2730】Ball 多项式exp 多项式ln 多项式开根 常系数线性递推 DP
- BZOJ 3625: [Codeforces Round #250]小朋友和二叉树 dp 生成函数 多项式开根
- [bzoj3625][Codeforces 250 E]The Child and Binary Tree(生成函数+多项式运算+FFT)
- 多项式求ln,求exp,开方,快速幂 学习总结
- [带标号无向连通图计数 容斥原理 多项式求逆 多项式求ln 模板题] BZOJ 3456 城市规划
- BZOJ 1004: [HNOI2008]Cards [Polya 生成函数DP]
- BZOJ3684:大朋友和多叉树(拉格朗日反演+多项式逆元+ntt)
- 【BZOJ】3456: 城市规划(多项式求ln)
- [生成函数][NTT][多项式求逆]BZOJ 3456: 城市规划
- BZOJ3684 大朋友和多叉树(多项式相关计算)
- [BZOJ2259]异化多肽(生成函数+NTT+多项式求逆)
- BZOJ 3456: 城市规划 [多项式求逆元 DP]
- [bzoj1812][IOI2006]riv_多叉树转二叉树_树形dp