969 字
5 分钟
BZOJ3684: 大朋友和多叉树
Description
我们的大朋友很喜欢计算机科学,而且尤其喜欢多叉树。对于一棵带有正整数点权的有根多叉树,如果它满足这样的性质,我们的大朋友就会将其称作神犇的:点权为1的结点是叶子结点;对于任一点权大于1的结点u,u的孩子数目deg[u]属于集合D,且u的点权等于这些孩子结点的点权之和。 给出一个整数s,你能求出根节点权值为s的神犇多叉树的个数吗?请参照样例以更好的理解什么样的两棵多叉树会被视为不同的。 我们只需要知道答案关于950009857(453*2^21+1,一个质数)取模后的值。
Input
第一行有2个整数s,m。 第二行有m个互异的整数,d[1],d[2],…,d[m],为集合D中的元素。
Output
输出一行仅一个整数,表示答案模950009857的值。
Sample Input
4 22 3
Sample Output
10
HINT
数据规模: , ,有3组小数据和3组大数据。
题解
求出树的生成函数
移一下项
设
为的复合逆
上拉格朗日反演
#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int MOD = 950009857;const int MAXN = 2e6 + 5;inline int read(){ int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f;}long long pow_mod(long long a, int b){ long long ans = 1; while (b) { if (b & 1) ans = ans * a % MOD; b >>= 1; a = a * a % MOD; } return ans;}int rev[MAXN];int Inv;void FFt(int *a, int N, int op){ int w, wn, t; for (int i = 1; i < N; i++) if (i < rev[i]) swap(a[i], a[rev[i]]); for (int k = 2; k <= N; k <<= 1) { wn = pow_mod(7, op == 1 ? (MOD - 1) / k : MOD - 1 - (MOD - 1) / k); for (int j = 0; j < N; j += k) { w = 1; for (int i = 0; i < (k >> 1); i++, w = 1ll * w * wn % MOD) { t = 1ll * a[i + j + (k >> 1)] * w % MOD; a[i + j + (k >> 1)] = (a[i + j] - t + MOD) % MOD; a[i + j] = (a[i + j] + t) % MOD; } } } if (op == -1) for (int i = 0; i < N; i++) a[i] = 1ll * a[i] * Inv % MOD;}int tmp[MAXN];void Get_Inv(int dep, int *a, int *b){ if (dep == 1) return b[0] = pow_mod(a[0], MOD - 2), void(); Get_Inv((dep + 1) >> 1, a, b); int N = 1; while (N < (dep << 1)) N <<= 1; Inv = pow_mod(N, MOD - 2); for (int i = 1; i < N; i++) if (i & 1) rev[i] = (rev[i >> 1] >> 1) | (N >> 1); else rev[i] = (rev[i >> 1] >> 1); //copy(a, a + dep, tmp); for (int i = 0; i < dep; i++) tmp[i] = a[i]; for (int i = dep; i < N; i++) tmp[i] = 0; //fill(tmp + dep, tmp + N, 0); FFt(tmp, N, 1); FFt(b, N, 1); for (int i = 0; i < N; i++) b[i] = 1ll * b[i] * ((2 - 1ll * b[i] * tmp[i] % MOD + MOD) % MOD) % MOD; FFt(b, N, -1); for (int i = dep; i < N; i++) b[i] = 0; //fill(b + dep, b + N, 0);}int G[MAXN], Ans[MAXN], C[MAXN];int main(){ int n = read(), m = read(); C[0] = 1; for (int i = 1; i <= m; i++) C[read() - 1] = MOD - 1; Get_Inv(n, C, G); Ans[0] = 1; int b = n; int N = 1; while (N < 2 * n) N <<= 1; Inv = pow_mod(N, MOD - 2); for (int i = 1; i < N; i++) if (i & 1) rev[i] = (rev[i >> 1] >> 1) | (N >> 1); else rev[i] = (rev[i >> 1] >> 1); while (b) { if (b & 1) { FFt(Ans, N, 1); FFt(G, N, 1); for (int i = 0; i < N; i++) Ans[i] = 1ll * Ans[i] * G[i] % MOD; FFt(Ans, N, -1); FFt(G, N, -1); for (int i = n; i < N; i++) Ans[i] = 0; } b >>= 1; FFt(G, N, 1); for (int i = 0; i < N; i++) G[i] = 1ll * G[i] * G[i] % MOD; FFt(G, N, -1); for (int i = n; i < N; i++) G[i] = 0; // fill(G + n, G + N, 0); } printf ("%d\n", 1ll * Ans[n - 1] * pow_mod(n, MOD - 2) % MOD);}