今天才知道莫比乌斯反演还可以这样:$$F(n)=sum_{n|d}f(d) Rightarrow f(n)=sum_{n|d}mu(frac{d}{n})F(d)$$我好弱,,,对于$$F(i)=left lfloor frac{n}{i} ight floorleft lfloor frac{m}{i} ight floor$$反演后$$f(i)=sum_{i|d}mu(frac{d}{i})F(d)=sum_{i|d}mu(frac{d}{i})left lfloor frac{n}{i} ight floorleft lfloor frac{m}{i} ight floor$$因为$left lfloor frac{n}{i} ight floorleft lfloor frac{m}{i} ight floor$的取值是$O(2(sqrt{n}+sqrt{m})$的,所以除法枚举这些取值再乘上区间内的$mu$值就可以做到$O(nsqrt{n})$时间内解决所有询问,区间内的$mu$值用前缀和相减就可以了
#include<cstdio> #include<cstring> #include<algorithm> #define read(x) x=getint() using namespace std; const int N = 50000; int getint() { int k = 0, fh = 1; char c = getchar(); for(; c < '0' || c > '9'; c = getchar()) if (c == '-') fh = -1; for(; c >= '0' && c <= '9'; c = getchar()) k = k * 10 + c - '0'; return k * fh; } bool check[N + 3]; int prime[N + 3], mu[N + 3], sum[N + 3]; void shai() { memset(check, 0, sizeof(check)); sum[1] = 1; mu[1] = 1; int num = 0; for(int i = 2; i <= N; ++i) { if (!check[i]) { prime[++num] = i; mu[i] = -1; } for(int j = 1; j <= num; ++j) { if (i * prime[j] > N) break; check[i * prime[j]] = 1; if (i % prime[j] == 0) {mu[i * prime[j]] = 0; break;} else mu[i * prime[j]] = - mu[i]; } sum[i] = sum[i - 1] + mu[i]; } } long long Q(int n, int m) { if (n > m) swap(n, m); long long ret = 0; for(int i = 1, la = 0; i <= n; i = la + 1) { la = min(n / (n / i), m / (m / i)); ret += (long long) (sum[la] - sum[i - 1]) * (n / i) * (m / i); } return ret; } int main() { shai(); int a, b, c, d, k, T; long long QQ; read(T); while (T--) { read(a); read(b); read(c); read(d); read(k); QQ = Q(b / k, d / k) - Q((a - 1) / k, d / k) - Q(b / k, (c - 1) / k) + Q((a - 1) / k, (c - 1) / k); printf("%lld ", QQ); } return 0; }
233