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'=lfloorfrac{a}{d}
floor,b'=lfloorfrac{b}{d}
floor,a'le b'$
那么题目要求的是$sum_{xle a',yle b'}left[gcdleft(x,y
ight)=1
ight]$
设$f(n)$表示$sum_{xle a',yle b'}left[gcdleft(x,y
ight)=n
ight]$
和$g(n)$表示$sum_{xle a',yle b'}left[nmid gcdleft(x,y
ight)
ight]$
那么答案就是$f(1)$
又显然$g(n)=lfloorfrac{a'}{n}
floorlfloorfrac{b'}{n}
floor$
并且满足关系式$g(n)=sum_{dle a',nmid d}f(d)$
反演得$f(n)=sum_{dle a', nmid d}muleft(frac{d}{n}
ight)g(d)=sum_{dle a', nmid d}muleft(frac{d}{n}
ight)lfloorfrac{a'}{d}
floorlfloorfrac{b'}{d}
floor$
将$n=1$代入得到$f(1)=sum_{dle a'}muleft(d
ight)lfloorfrac{a'}{d}
floorlfloorfrac{b'}{d}
floor$
但这样单次询问是$Oleft(n
ight)$的,仍需要优化
观察式(ti)子(jie)可以发现
后面两个向下取整在一段区间内不会变,如果我们处理出了$mu$的前缀和就可以一段一段的求解
又因为只有$Oleft(sqrt{n}
ight)$段
所以在$Oleft(n
ight)$的预处理后单次询问是$Oleft(sqrt{n}
ight)$的
所以总时间复杂度为$Oleft(nsqrt{n}
ight)$
#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 ", ans); } int main(){ fread(buf, sizeof(char), sizeof(buf), stdin); shai(); int n = readint(); while(n--) work(); return 0; }