CF235E Number Challenge

一、题目

点此看题

二、解法

建议做这道题之前先把 [SDOI 2015]约数个数和 做了,你就会发现有这样一个等价变换:
∑ i = 1 a ∑ j = 1 b ∑ k = 1 c ∑ x ∣ i ∑ y ∣ j ∑ z ∣ k [ ( x , y ) = 1 ] [ ( y , z ) = 1 ] [ ( x , z ) = 1 ] \sum_{i=1}^a\sum_{j=1}^b\sum_{k=1}^c\sum_{x|i}\sum_{y|j}\sum_{z|k}[(x,y)=1][(y,z)=1][(x,z)=1] i=1aj=1bk=1cxiyjzk[(x,y)=1][(y,z)=1][(x,z)=1]先枚举 x , y , z x,y,z x,y,z(写还是写作 i , j , k i,j,k i,j,k):
∑ i = 1 a ∑ j = 1 b ∑ k = 1 c a i × b j × c k × [ ( i , j ) = 1 ] [ ( j , k ) = 1 ] [ ( i , k ) = 1 ] \sum_{i=1}^a\sum_{j=1}^b\sum_{k=1}^c\frac{a}{i}\times\frac{b}{j}\times\frac{c}{k}\times[(i,j)=1][(j,k)=1][(i,k)=1] i=1aj=1bk=1cia×jb×kc×[(i,j)=1][(j,k)=1][(i,k)=1]然后改变一下枚举顺序:
∑ i = 1 a ∑ j = 1 b a i × b j × [ ( i , j ) = 1 ] ∑ k = 1 c c k × [ ( i j , k ) = 1 ] \sum_{i=1}^a\sum_{j=1}^b\frac{a}{i}\times\frac{b}{j}\times[(i,j)=1]\sum_{k=1}^c\frac{c}{k}\times[(ij,k)=1] i=1aj=1bia×jb×[(i,j)=1]k=1ckc×[(ij,k)=1]时间复杂度 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn)是允许的,前面不需要变化了,我们反演后面的部分:
f ( x ) = ∑ k = 1 c c k ∑ d ∣ ( x , k ) μ ( d ) f(x)=\sum_{k=1}^c\frac{c}{k}\sum_{d|(x,k)}\mu(d) f(x)=k=1ckcd(x,k)μ(d)先枚举 d d d
f ( x ) = ∑ d ∣ x μ ( d ) ∑ d ∣ k c k f(x)=\sum_{d|x}\mu(d)\sum_{d|k}\frac{c}{k} f(x)=dxμ(d)dkkc我们可以先预处理 ∑ d ∣ x c k \sum_{d|x}\frac{c}{k} dxkc,然后枚举 d d d,就可以算这个柿子了,前面的部分暴力算就行了,时间复杂度 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn)

#include <cstdio>
#include <iostream>
using namespace std;
const int N = 2005;
const int M = 4000005;
const int jzm = (1<<30)-1;
int read()
{
    int x=0,flag=1;
    char c;
    while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
    while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
    return x*flag;
}
int a,b,c,ans,cnt,p[N],mu[N],vis[N],f[M];
void sieve(int n)
{
    mu[1]=1;
    for(int i=2;i<=n;i++)
    {
        if(!vis[i]) p[++cnt]=i,mu[i]=-1;
        for(int j=1;j<=cnt && i*p[j]<=n;j++)
        {
            vis[i*p[j]]=1;
            if(i%p[j]==0) break;
            mu[i*p[j]]=-mu[i];
        }
    }
}
int gcd(int a,int b)
{
    return !b?a:gcd(b,a%b);
}
int main()
{
    a=read();b=read();c=read();
 if(a>b) swap(a,b);
 if(a>c) swap(a,c);
 if(b>c) swap(b,c);
    sieve(c);
    for(int i=1;i<=c;i++)
    {
        int s=0;
        for(int j=i;j<=c;j+=i)
            s+=c/j;
        for(int j=i;j<=a*b;j+=i)
            (f[j]+=mu[i]*s)&=jzm;
    }
    for(int i=1;i<=a;i++)
        for(int j=1;j<=b;j++)
            if(gcd(i,j)==1)
            {
                ans+=(1ll*(a/i)*(b/j)*f[i*j])&jzm;
                ans&=jzm;
            }
    printf("%d\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值