[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

Sample Output

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;
}

 

posted @ 2017-10-05 21:45  jzyy  阅读(306)  评论(0编辑  收藏  举报