您的当前位置:首页正文

「HAOI 2011」Problem b

2024-11-08 来源:个人技术集锦

Description

  求解(多组数据)

Answer=i=xnj=ym[gcd(i,j)=k] A n s w e r = ∑ i = x n ∑ j = y m [ gcd ( i , j ) = k ]

   1T,x,y,n,m,k5×104 1 ⩽ T , x , y , n , m , k ⩽ 5 × 10 4


Solution

  根据容斥原理,原式可以分成 4 4 块来处理,每一块的式子都为

i=1nj=1m[gcd(i,j)=k]

  考虑化简该式子

i=1nkj=1mk[gcd(i,j)=1] ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ [ gcd ( i , j ) = 1 ]

  因为 gcd(i,j)=1 gcd ( i , j ) = 1 时对答案才用贡献,于是我们可以将其替换为 ϵ(gcd(i,j)) ϵ ( gcd ( i , j ) ) ϵ(n) ϵ ( n ) 当且仅当 n=1 n = 1 时值为 1 1 否则为 0 ),故原式化为

i=1nkj=1mkϵ(gcd(i,j)) ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ ϵ ( gcd ( i , j ) )

  将 ϵ ϵ 函数展开得到

i=1nkj=1mkd|gcd(i,j)μ(d) ∑ i = 1 ⌊ n k ⌋ ∑ j = 1 ⌊ m k ⌋ ∑ d | gcd ( i , j ) μ ( d )

  变换求和顺序,先枚举 d|gcd(i,j) d | g c d ( i , j ) 可得

d=1nkμ(d)i=1nkd|ij=1mkd|j ∑ d = 1 ⌊ n k ⌋ μ ( d ) ∑ i = 1 ⌊ n k ⌋ d | i ∑ j = 1 ⌊ m k ⌋ d | j

  (其中 d|i d | i 表示 i i d 的倍数时对答案有 1 1 的贡献)
  易知 1nk d d 的倍数有 nkd 个,故原式化为

d=1nkμ(d)nkdmkd ∑ d = 1 ⌊ n k ⌋ μ ( d ) ⌊ n k d ⌋ ⌊ m k d ⌋

  很显然,式子可以数论分块求解(注意:过程中默认 nm n ⩽ m )。

  时间复杂度 Θ(N+Tn) Θ ( N + T n )


Code

#include <cstdio>
#include <algorithm>
const int N=50000;
int mu[N+5],p[N+5];
bool flg[N+5];
void init() {
    int tot=0;
    mu[1]=1;
    for(int i=2;i<=N;++i) {
        if(!flg[i]) {
            p[++tot]=i;
            mu[i]=-1;
        }
        for(int j=1;j<=tot&&i*p[j]<=N;++j) {
            flg[i*p[j]]=1;
            if(i%p[j]==0) {
                mu[i*p[j]]=0;
                break;
            }
            mu[i*p[j]]=-mu[i];
        }
    }
    for(int i=1;i<=N;++i) mu[i]+=mu[i-1];
}
int solve(int n,int m) {
    int res=0;
    for(int i=1,j;i<=std::min(n,m);i=j+1) {
        j=std::min(n/(n/i),m/(m/i));
        res+=(mu[j]-mu[i-1])*(n/i)*(m/i);
    }
    return res;
}
int main() {
    int T,a,b,c,d,k;
    init();
    for(scanf("%d",&T);T;--T) {
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        printf("%d\n",solve(b/k,d/k)-solve(b/k,(c-1)/k)-solve((a-1)/k,d/k)+solve((a-1)/k,(c-1)/k));
    }
    return 0;
}
Top