BZOJ 3529: [Sdoi2014]数表
二次联通门 : BZOJ 3529: [Sdoi2014]数表
Latex的公式写起来略麻烦。。。
设$\LARGE F(x)=\sum_{d|x} \lfloor \dfrac xd \rfloor$
$\LARGE Answer = \sum_{i=1}^n\sum_{j=1}^{m}F(\gcd(i,j))*[F(\gcd(i,j)<=a]$
当没有$a$的限制时
$\LARGE Answer = \sum_{d=1}^{n}\sum_{i=1}^{n}\sum_{j=1}^{m}F(d)*[\gcd(i,j)=d]$
$\LARGE=\sum_{d=1}^{n}F(d)\sum_{i=1}^{n/d}\sum_{j=1}^{m/d}[\gcd(i,j)=1]$
由莫比乌斯反演得(见BZOJ 1101: [POI2007]Zap):
$\LARGE Answer = \sum_{d=1}^{n}F(d)\sum_{i=1}^n\mu(i)\lfloor \dfrac {n}{id} \rfloor \lfloor \dfrac {m}{id} \rfloor $
$\LARGE =\sum_{d=1}^{n}\lfloor \dfrac nd \rfloor \lfloor \dfrac md \rfloor\sum_{i|d}\mu(\dfrac ni)F(i)$
然后对于 $\LARGE \sum_{d=1}^{n}\lfloor \dfrac nd \rfloor \lfloor \dfrac md \rfloor$可以$O(n\sqrt n)$计算
后一部分也可以预先预处理出来
再把$a$的限制加上
离线处理这个问题
将$F$和$a$都排个序,那么要计算的就是$F(i)<=a$的$i$
那么就可以用树状数组维护$\LARGE \sum_{i|d}\mu(\dfrac ni)F(i)$的前缀和,每次插入小于当前$a$的$F[i]$,后每次询问前缀和即可
/* BZOJ 3529: [Sdoi2014]数表 树状数组+莫比乌斯反演 */ #include <cstdio> #include <iostream> #include <algorithm> #define rg register inline void read (int &n) { rg char c = getchar (); for (n = 0; !isdigit (c); c = getchar ()); for (; isdigit (c); n = n * 10 + c - '0', c = getchar ()); } #define Max 100000 int t[Max + 9], p[Max + 9], mu[Max + 9]; bool is[Max + 9]; inline int min (int a, int b) { return a < b ? a : b; } void Euler (int N) { rg int i, j, k; int C = 0; mu[1] = 1; for (i = 2; i <= N; ++ i) { if (!is[i]) p[++ C] = i, mu[i] = -1; for (j = 1; j <= C; ++ j) { if ((k = i * p[j]) > N) break; is[k] = true; if (i % p[j] == 0) { mu[k] = 0; break; } else mu[k] = -mu[i]; } } } struct Q { int x, y; Q (int a = 0, int b = 0) : x (a), y (b) { } bool operator < (const Q &rhs) const { return x == rhs.x ? y < rhs.y : x < rhs.x; } } f[Max + 9]; void F () { rg int i, j; for (i = 1; i <= Max; ++ i) for (j = i; j <= Max; j += i) f[j].x += i; for (i = 1; i <= Max; ++ i) f[i].y = i; std :: sort (f + 1, f + 1 + Max); } struct D { int n, m, a, id; bool operator < (const D &rhs) const { return a < rhs.a; } } q[Max + 9]; int Answer[Max + 9]; inline int Query (int x) { int res = 0; for (rg int i = x; i; i -= i & (-i)) res += t[i]; return res; } void Change (int x, int k) { for (rg int i = x; i <= Max; i += i & (-i)) t[i] += k; } int Ask (int N, int M) { int res = 0, j; if (N > M) std :: swap (N, M); for (rg int i = 1; i <= N; i = j + 1) { j = min (N / (N / i), M / (M / i)); res += (N / i) * (M / i) * (Query (j) - Query (i - 1)); } return res & 0x7fffffff; } int main (int argc, char *argv[]) { int N, M; rg int i, j, k; Euler (Max); F (); int T; read (T); for (i = 1; i <= T; ++ i) read (q[i].n), read (q[i].m), read (q[i].a), q[i].id = i; std :: sort (q + 1, q + 1 + T); for (i = 1, j = 1; i <= T; ++ i) { for (; j <= Max && f[j].x <= q[i].a; ++ j) for (k = f[j].y; k <= Max; k += f[j].y) Change (k, f[j].x * mu[k / f[j].y]); Answer[q[i].id] = Ask (q[i].n, q[i].m); } for (i = 1; i <= T; ++ i) printf ("%d\n", Answer[i]); return 0; }