[BZOJ1101][POI2007]Zap
1101: [POI2007]Zap
Time Limit: 10 Sec Memory Limit: 162 MB Submit: 2732 Solved: 1164 [Submit][Status][Discuss]Description
FGD正在破解一段密码,他需要回答很多类似的问题:对于给定的整数a,b和d,有多少正整数对x,y,满足x<=a ,y<=b,并且gcd(x,y)=d。作为FGD的同学,FGD希望得到你的帮助。
Input
第一行包含一个正整数n,表示一共有n组询问。(1<=n<= 50000)接下来n行,每行表示一个询问,每行三个 正整数,分别为a,b,d。(1<=d<=a,b<=50000)
Output
对于每组询问,输出到输出文件zap.out一个正整数,表示满足条件的整数对数。
Sample Input
2
4 5 2
6 4 3
4 5 2
6 4 3
Sample Output
3
2
//对于第一组询问,满足条件的整数对有(2,2),(2,4),(4,2)。对于第二组询问,满足条件的整数对有(
6,3),(3,3)。
2
//对于第一组询问,满足条件的整数对有(2,2),(2,4),(4,2)。对于第二组询问,满足条件的整数对有(
6,3),(3,3)。
不妨设$a'=\lfloor\frac{a}{d}\rfloor,b'=\lfloor\frac{b}{d}\rfloor,a'\le b'$
那么题目要求的是$\sum_{x\le a',y\le b'}\left[gcd\left(x,y\right)=1\right]$
设$f(n)$表示$\sum_{x\le a',y\le b'}\left[gcd\left(x,y\right)=n\right]$
和$g(n)$表示$\sum_{x\le a',y\le b'}\left[n\mid gcd\left(x,y\right)\right]$
那么答案就是$f(1)$
又显然$g(n)=\lfloor\frac{a'}{n}\rfloor\lfloor\frac{b'}{n}\rfloor$
并且满足关系式$g(n)=\sum_{d\le a',n\mid d}f(d)$
反演得$f(n)=\sum_{d\le a', n\mid d}\mu\left(\frac{d}{n}\right)g(d)=\sum_{d\le a', n\mid d}\mu\left(\frac{d}{n}\right)\lfloor\frac{a'}{d}\rfloor\lfloor\frac{b'}{d}\rfloor$
将$n=1$代入得到$f(1)=\sum_{d\le a'}\mu\left(d\right)\lfloor\frac{a'}{d}\rfloor\lfloor\frac{b'}{d}\rfloor$
但这样单次询问是$O\left(n\right)$的,仍需要优化
观察式(ti)子(jie)可以发现
后面两个向下取整在一段区间内不会变,如果我们处理出了$\mu$的前缀和就可以一段一段的求解
又因为只有$O\left(\sqrt{n}\right)$段
所以在$O\left(n\right)$的预处理后单次询问是$O\left(\sqrt{n}\right)$的
所以总时间复杂度为$O\left(n\sqrt{n}\right)$
#pragma GCC optimize("O2") #include <cstdio> #include <algorithm> using namespace std; char buf[10000000], *ptr = buf - 1; inline int readint(){ int n = 0; while(*++ptr < '0' || *ptr > '9'); while(*ptr <= '9' && *ptr >= '0') n = (n << 1) + (n << 3) + (*ptr++ & 15); return n; } const int maxn = 50000 + 10; bool mark[maxn] = {false}; int mu[maxn], sum[maxn]; int pri[maxn], prn = 0; void shai(){ mu[1] = 1; for(int i = 2; i <= 50000; i++){ if(!mark[i]){ pri[++prn] = i; mu[i] = -1; } for(int j = 1; j <= prn && pri[j] * i <= 50000; j++){ mark[i * pri[j]] = true; if(i % pri[j] == 0){ mu[i * pri[j]] = 0; break; } else mu[i * pri[j]] = -mu[i]; } } sum[0] = 0; for(int i = 1; i <= 50000; i++) sum[i] = sum[i - 1] + mu[i]; } int a, b, d; inline void work(){ a = readint(); b = readint(); d = readint(); a /= d; b /= d; if(a > b) swap(a, b); int ans = 0, pos; for(int i = 1; i <= a; i = pos + 1){ pos = min(a / (a / i), b / (b / i)); ans += (sum[pos] - sum[i - 1]) * (a / i) * (b / i); } printf("%d\n", ans); } int main(){ fread(buf, sizeof(char), sizeof(buf), stdin); shai(); int n = readint(); while(n--) work(); return 0; }