模拟赛-树
树
题意
求满足下面条件有根树的个数。同构的算一种
- 叶子节点的权值是某个 \(a_i\) 的倍数
- 节点的权值为其子树节点的权值之积
- 根节点权值不超过 \(n\)。
- 每个节点要么没有儿子,要么有两个以上儿子
\(n\le 10 ^ {10}, m\le 8, 2\le a_i \le 200\)
题解
\(p(x) = 1\) 当且仅当存在一个 \(i\) 使得 \(a_i | x\),令答案的函数为 \(f\),则
\[f = p + \sum_{i \ge 2}f ^ i = p + \frac{f ^ 2}{1 - f}
\]
于是则有
\[f = \frac{2f ^ 2 + p}{1 + p}
\]
这里面的除法是迪利克雷逆,乘法是迪利克雷卷积。
首先我们可以 \(O(L \log L)\) 的预处理出来 \(L\) 之内的 \(f, f ^ 2, (p + 1) ^ {-1}\) 函数的前缀和。
我们考虑怎么算大的范围,首先 \(p\) 的前缀和可以用 \(2 ^ m\) 来暴力容斥,然后 \((p + 1) ^ {-1}\) 就是和 \(p + 1\) 这个函数卷积用杜教筛。
之后 \(f ^ 2\) 的前缀和也是类似于整除分块的东西,就是 \(sf^2(n) = \sum_{i = 1} ^ n sf(\frac{n}{i}) f(i)\) 。然后 \(f\) 的前缀和就按照那个式子算
\[sf(n) = \sum_{i = 1} ^ n (1 + p) ^ {-1} \left(\frac{n}{i}\right) \times (f^2(i) + p(i))
\]
// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
mt19937 eng;
void init(int Seed) {return eng.seed(Seed);}
int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++)
template<typename T>
inline void read(T &x) {
x = 0;
bool f = 0;
char ch = gc();
while (!isdigit(ch)) f = ch == '-', ch = gc();
while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
if (f) x = -x;
}
inline void reads(char *s) {
char ch = gc();
while (isspace(ch)) ch = gc();
while (!isspace(ch) && ch != EOF) *(s++) = ch, ch = gc();
*s = 0;
return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
read(x);
read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 262144, MOD = 1e9 + 7, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
if (y < 0) {
y %= MOD - 1;
y += MOD - 1;
}
int ret = 1;
for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
return ret;
};
auto Mod = [] (int x) -> int {
if (x >= MOD) return x - MOD;
else if (x < 0) return x + MOD;
else return x;
};
template<const int N_num, const int M_num>
struct Graph {
int H[N_num];
struct Edge {int to, lac;} e[M_num];
inline void add_edge(int x, int y) {e[*H] = {y, H[x]};H[x] = (*H)++;}
inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
using ull = unsigned long long;
void add(int &x, int y) {if ((x += y) >= MOD) x -= MOD;}
const int MAXM = 2e6 + 10;
ll N;
int M, A[10], sp[MAXM], sip1[MAXM], sf2[MAXM], sf[MAXM];
ll prod[256];
inline ll gcd(ll x, ll y) {
for (; y; x %= y, swap(x, y));
return x;
}
std::unordered_map<ll, int> m_sp, m_sip1, m_sf, m_sf2;
int calc_sp(ll n) {
if (n < MAXM) return sp[n];
if (m_sp.count(n)) return m_sp[n];
int &res = m_sp[n] = 0;
for (int i = 1; i < 1 << M; ++i) add(res, ((__builtin_popcount(i) & 1) ? 1LL : MOD - 1LL) * (n / prod[i] % MOD) % MOD);
return res;
}
int calc_sip1(ll n) {
if (n < MAXM) return sip1[n];
if (m_sip1.count(n)) return m_sip1[n];
// O(n);
int &res = m_sip1[n] = 1;
for (ll l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
add(res, (ll) Mod(calc_sp(l - 1) - calc_sp(r)) * calc_sip1(n / l) % MOD);
}
return res;
}
int calc_sf(ll n);
int calc_sf2(ll n) {
if (n < MAXM) return sf2[n];
if (m_sf2.count(n)) return m_sf2[n];
int &res = m_sf2[n] = 0;
// O(n);
for (ll l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
add(res, (ll) Mod(calc_sf(r) - calc_sf(l - 1)) * calc_sf(n / l) % MOD);
}
return res;
}
int calc_sf(ll n) {
// O(n);
if (n < MAXM) return sf[n];
if (m_sf.count(n)) return m_sf[n];
int &res = m_sf[n] = 0;
for (ll l = 2, r; l <= n; l = r + 1) {
r = n / (n / l);
add(res, (ll) Mod(2 * Mod(calc_sf2(r) - calc_sf2(l - 1)) % MOD + Mod(calc_sp(r) - calc_sp(l - 1))) * calc_sip1(n / l) % MOD);
}
return res;
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
// std::ios::sync_with_stdio(0);
// cout << std::fixed << std::setprecision(8);
// cin.tie(0);
// cout.tie(0);
qwq::init(20050112);
read(N, M);
for (int i = 1; i <= M; ++i) read(A[i]);
sort(A + 1, A + 1 + M);
M = unique(A + 1, A + 1 + M) - A - 1;
for (int i = 1; i <= M; ++i) for (int j = A[i]; j < MAXM; j += A[i]) sp[j] = 1;
sip1[1] = 1;
for (int i = 1; i < MAXM; ++i) for (int j = i + i; j < MAXM; j += i) sip1[j] = Mod(sip1[j] - sip1[i] * sp[j / i]);
for (int i = 1; i < MAXM; ++i) {
for (int j = 1; j * i < MAXM; ++j) {
add(sf[i * j], (2LL * sf2[i] + sp[i]) * sip1[j] % MOD);
if (j <= i) add(sf2[i * j], (i == j ? 1LL : 2LL) * sf[i] * sf[j] % MOD);
}
}
for (int i = 1; i < MAXM; ++i) add(sp[i], sp[i - 1]), add(sip1[i], sip1[i - 1]), add(sf[i], sf[i - 1]), add(sf2[i], sf2[i - 1]);
for (int i = 1; i < 1 << M; ++i) {
prod[i] = 1;
for (int j = 0; j < M; ++j) if ((i >> j) & 1) prod[i] = prod[i] / gcd(A[j + 1], prod[i]) * A[j + 1];
}
// O(sip1[8]);
printf("%d\n", calc_sf(N));
// cout << (-3 / 2);
cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
return (0-0);
}