1. 程式人生 > >[BZOJ1101][POI2007]Zap

[BZOJ1101][POI2007]Zap

min 預處理 sum zoj () printf sqrt scrip scu

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

[BZOJ1101][POI2007]Zap