[POI2007] ZAP-Queries (莫比烏斯反演)

[POI2007] ZAP-Queries

題目描述

Byteasar the Cryptographer works on breaking the code of BSA (Byteotian Security Agency). He has alreadyfound out that whilst deciphering a message he will have to answer multiple queries of the form"for givenintegers aa, bb and dd, find the number of integer pairs (x,y)(x,y) satisfying the following conditions:html

1\le x\le a1≤x≤a,1\le y\le b1≤y≤b,gcd(x,y)=dgcd(x,y)=d, where gcd(x,y)gcd(x,y) is the greatest common divisor of xx and yy".c++

Byteasar would like to automate his work, so he has asked for your help.優化

TaskWrite a programme which:spa

reads from the standard input a list of queries, which the Byteasar has to give answer to, calculates answers to the queries, writes the outcome to the standard output.code

FGD正在破解一段密碼,他須要回答不少相似的問題:對於給定的整數a,b和d,有多少正整數對x,y,知足x<=a,y<=b,而且gcd(x,y)=d。做爲FGD的同窗,FGD但願獲得你的幫助。orm

輸入輸出格式

輸入格式:htm

The first line of the standard input contains one integer nn (1\le n\le 50 0001≤n≤50 000),denoting the number of queries.blog

The following nn lines contain three integers each: aa, bb and dd(1\le d\le a,b\le 50 0001≤d≤a,b≤50 000), separated by single spaces.three

Each triplet denotes a single query.ip

輸出格式:

Your programme should write nn lines to the standard output. The ii'th line should contain a single integer: theanswer to the ii'th query from the standard input.

輸入輸出樣例

輸入樣例#1:

2
4 5 2
6 4 3

輸出樣例#1:

3
2

Solution

預備知識:莫比烏斯反演,整除分塊

不會的看這位dalao的博客莫比烏斯反演

本蒟蒻的整除分塊

根據題意
\[ans=\sum_{i=1}^a \sum_{j=1}^b [{gcd(i,j)=d}]\]
\[ans=\sum_{i=1}^{a/d} \sum_{j=1}^{b/d}[gcd(i,j)=1]\]

下面就是反演

\[ans=\sum_{i=1}^{a/d} \sum_{j=1}^{b/d} \sum_{p|gcd(i,j)}\mu(p)\]

可是這樣枚舉仍是\(O(n^2)\),因此咱們換一個變量枚舉,把最後一個求和提到前面,由於p既是i的因子又是j的因子,因此枚舉範圍就是\(min(a/d,b/d)\),那麼繼續推公式

\[ans=\sum_{p=1}^{min(a/d,b/d)}{\mu(p)} \sum_{i=1}^{a/d} \sum_{j=1}^{b/d} \lfloor\frac{a}{p\times d} \rfloor \lfloor\frac{b}{p\times d}\rfloor\]

若是對於後面的式子不理解,能夠這麼看,令\(x=a/d,y=b/d\)
\(p\)\(x,y\)的一個因子,在\(x\)的範圍內有\(\lfloor\frac{x}{p}\rfloor\)\(p\)的倍數,對於\(y\)同理,因此每一個因子\(p\)都有\(\lfloor\frac{x}{p}\rfloor\lfloor\frac{y}{p}\rfloor\)的貢獻

而對於後面的兩個求和咱們是能夠用前綴和預處理出來的,這個時候是能夠作到\(O(n)\)了,可是因爲多組數據,因此咱們發現,對於一段連續的p,由於a和b的值是肯定的,因此\(\lfloor\frac{a}{p\times d}\rfloor\lfloor\frac{b}{p\times d}\rfloor\)的值也是肯定的,這中間有許多重複的值,那麼咱們就可使用整除分塊優化到\(O(\sqrt n)\)

(有錯誤歡迎指出)

Code

#include<bits/stdc++.h>
#define lol long long
#define il inline
#define rg register
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)

using namespace std;

const int N=5e4+10;

void in(int &ans)
{
    ans=0; int f=1; char i=getchar();
    while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0', i=getchar();
    ans*=f;
}

int n,m,d,tot,ans,T;
int mu[N],sum[N],prime[N];
bool vis[N];

il void get_mu() {
    mu[1]=1;
    for(int i=2;i<=N-10;i++) {
        if(!vis[i]) prime[++tot]=i,mu[i]=-1;
        for(int j=1;j<=tot && prime[j]*i<=N-10;j++) {
            vis[i*prime[j]]=1;
            if(i%prime[j]==0) break;
            else mu[i*prime[j]]=-mu[i];
        }
    }
    for(int i=1;i<=N-10;i++) sum[i]=sum[i-1]+mu[i];
}

int main()
{
    in(T); get_mu();
    while(T--) {
        in(n),in(m),in(d); int nn=n/=d,mm=m/=d,ans=0;
        for(rg int i=1,pos,p=Min(n,m);i<=p;i=pos+1) {
            pos=Min(n/(n/i),m/(m/i));
            ans+=(sum[pos]-sum[i-1])*(nn/i)*(mm/i);
        }
        printf("%d\n",ans);
    }
    return 0;
}

博主蒟蒻,隨意轉載.但必須附上原文連接

http://www.cnblogs.com/real-l/

相關文章
相關標籤/搜索