1. 程式人生 > 實用技巧 >洛谷P3455 [POI2007]ZAP-Queries

洛谷P3455 [POI2007]ZAP-Queries

題目連結

https://www.luogu.com.cn/problem/P3455

思路

簡單來說,就是求\(\displaystyle \sum^{a}_{i=1}{\sum^{b}_{j=1}{[gcd(i,j)==d}]}\),不妨令a<b。

那麼很容易想到,將\(a\)\(b\)都除以\(d\),就只要求互質的數對了,就是\(\displaystyle \sum^{a/d}_{i=1}{\sum^{b/d}_{j=1}{[gcd(i,j)==1]}}\)

然後就是反演題常規套路,列舉g=gcd(i,j),一通亂搞搞出\(\displaystyle \sum^{a}_{D=1}{\lfloor \frac{a}{D}\rfloor \lfloor \frac{b}{D}\rfloor}\)

這個東西,再加上整除分塊就搞定了。

程式碼

#include<cstdlib>
#include<algorithm>
#define maxn (int)(5*1e4+10)
using namespace std;
int u[maxn],book[maxn],s[maxn],p[maxn],cnt=0;
int main(){
    int n,i,j,a,b,d,l,r;
    scanf("%d",&n);
    u[0]=0;u[1]=1;
    for(i=2;i<maxn;++i){
        if(!book[i]){
            u[i]=-1;
            p[++cnt]=i;
        }
        for(j=1;p[j]*i<maxn&&j<=cnt;++j){
            book[i*p[j]]=1;
            if(!(i%p[j])) break;
            u[i*p[j]]=-u[i];
        }
    }
    for(i=1;i<maxn;++i)
        s[i]=s[i-1]+u[i];
    for(i=1;i<=n;++i){
        scanf("%d%d%d",&a,&b,&d);
        if(a>b) swap(a,b);
        a/=d;b/=d;
        int ans=0;
        for(l=r=1;l<=a;l=r+1,r=min(a/(a/l),b/(b/l))){
            ans+=(a/l)*(b/l)*(s[r]-s[l-1]);
            if(r==a) break;
        }
        printf("%d\n",ans);
    }
    // system("pause");
    return 0;
}