WC2014时空穿梭 反演

[WC2014]时空穿梭 如果推式子足够熟练的话,可以一路推出来的。但是我推着推着就迷惑了。。 首先要找的是直线。那么确定直线最好的办法是确定两个端点。考虑枚举两个点,坐标分别为$(a_1,a_2…),(b_1,b_2,…)$,那么必须有$\exists i, a_i < b_i$。确定两个点之后,这条线上一共有$gcd_{i=1}^n(b_i-a_i)+1$个点。 那么答案为: $$ans=\sum_{a,b} \binom{gcd(b_i-a_i)-1}{c-2}$$ 对这个式子进行优化。首先我们只关心坐标差,枚举坐标差$\Delta_i = b_i-a_i$。 $$\sum_{1 \leq \Delta_i < m_i} \binom{gcd(\Delta_i )-1}{c - 2} \prod_{i \leq n} (m_i - \Delta_i) $$ 紧接着再枚举GCD为$g$ $$\sum_{g < min_m} \binom{g-1}{c-2} \sum_{ 1 \leq \Delta_i < m_i,gcd(\Delta_i) = g}\prod_{i \leq n} (m_i - \Delta_i)$$ 令后面这一坨为$f(g)$。为了去掉GCD的限制,考虑反演 $$f’(g) = \sum_{g d} f(d) = \sum_{ 1 \leq \Delta_i < m_i, g gcd(\Delta_i) }\prod_{i \leq n} (m_i - \Delta_i)$$ 那么GCD就可以去掉了。 $$\sum_{1 \leq \Delta_i < \lfloor \frac{m_i}{g} \rfloor} \prod(m_i - \Delta_ig)$$ 实际上考虑分配律这个式子可以把两个符号换一下位置。交换后后面是$\sum$,可以用等差数列求和公式。化简之后如下: $$f’(g)=\prod_{i \leq n} (\lfloor \frac{m_i - 1}{g} \rfloor m_i - \frac{d\lfloor \frac{m_i - 1}{g} \rfloor(1+\lfloor \frac{m_i - 1}{g} \rfloor)}{2}) $$ 反演的式子套进去,直接带入答案: $$ans=\sum_{g < min_m} \binom{g-1}{c-2} \sum_{g d} \mu(\frac{d}{g}) \prod_{i \leq n} (\lfloor \frac{m_i - 1}{d} \rfloor m_i - \frac{d\lfloor \frac{m_i - 1}{d} \rfloor(1+\lfloor \frac{m_i - 1}{d} \rfloor)}{2})$$ 有个取整,考虑取整的可能值是$O(n \sqrt m)$的。枚举所有可能的$\lfloor \frac{m_i - 1}{d} \rfloor$,看作常数,后面是一个$n$次多项式。暴力拆开 $$\sum_{0 \leq i \leq n} v_i \sum_{?\leq d \leq ?}d^i \sum_{g d} \mu(\frac{d}{g}) \binom{g-1}{c-2} $$ 对于这两个$?$,它们取决于取整的$d$的范围。那么后面这一堆$\sum$只取决于$c,?,i$,预处理出来就好了。 复杂度:$O(cmlogm+cmn+T\sqrt m n^3)$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <bits/stdc++.h>

const int N = 12, C = 21, M = 100001;
const int P = 10007, INF = 1e9;

inline int fpow(int x, int y) {
int ret = 1;
for ( ; y; y >>= 1, x = x * x % P)
if (y & 1) ret = ret * x % P;
return ret;
}

int fac[M], inv[M];

inline int binom(int n, int m) {
if (n < 0 m < 0 n < m)
return 0;
if (n >= P m >= P)
return binom(n / P, m / P) * binom(n % P, m % P) % P;
return fac[n] * inv[m] % P * inv[n - m] % P;
}

int S[C][M][N];
int mu[M];

inline void init() {
fac[0] = 1;
for (int i = 1; i < P; ++i)
fac[i] = fac[i - 1] * i % P;
inv[P - 1] = fpow(fac[P - 1], P - 2);
for (int i = P - 1; i; --i)
inv[i - 1] = inv[i] * i % P;

mu[1] = 1;
for (int i = 1; i < M; ++i)
for (int j = i + i; j < M; j += i)
mu[j] -= mu[i];

static int f[M];

for (int c = 2; c < C; ++c) {
memset(f, 0, sizeof(f));
for (int g = c - 1; g < M; ++g)
for (int d = g; d < M; d += g)
f[d] = (f[d] + mu[d / g] * binom(g - 1, c - 2)) % P;
for (int d = 1; d < M; ++d)
for (int i = 0, v = 1; i < N; ++i, v = d % P * v % P)
S[c][d][i] = (S[c][d - 1][i] + f[d] * v % P + P) % P;
}
}

int n, c, m[N];

inline void solve() {
int min_m = INF;
std::cin >> n >> c;
for (int i = 1; i <= n; ++i) {
std::cin >> m[i];
min_m = std::min(min_m, m[i]);
}

if (min_m < c) {
puts("0");
return;
}

std::vector<int> div;
div.push_back(1);
for (int i = 1; i <= n; ++i) {
int tmp = 1;
while (tmp < m[i] - 1) {
tmp = (m[i] - 1) / ((m[i] - 1) / tmp) + 1;
div.push_back(tmp);
}
}

std::sort(div.begin(), div.end());
div.resize(std::unique(div.begin(), div.end()) - div.begin());
div.push_back(min_m);

int ans = 0;

for (int i = 0; i + 1 < div.size(); ++i) {
if (div[i] >= min_m) {
break;
}
int l = div[i], r = div[i + 1] - 1;

std::vector<int> f(n + 1);
f[0] = 1;
for (int j = 1; j <= n; ++j) {
int v = (m[j] - 1) / l % P, b = 1ll * v * m[j] % P,
a = -1ll * v * (v + 1) % P * inv[2] % P;

for (int k = n; k; --k) {
f[k] = (f[k - 1] * a + f[k] * b) % P;
}
f[0] = (f[0] * b) % P;

}

for (int j = 0; j <= n; ++j) {
ans = (ans + f[j] * (S[c][r][j] - S[c][l - 1][j])) % P;
}
}

ans = (ans % P + P) % P;
std::cout << ans << std::endl;
}

int main() {
init();
int T;
std::cin >> T;
while (T--) {
solve();
}
return 0;
}