BZOJ2301 Problemb

这道题目就是求 x=aby=cd[gcd(x,y)=k] 。其中数据范围全是 5×105 级别
这个显然就是莫比乌斯反演。我们可以先用前缀和转化一下,然后题目就变成了求

f(i)=x=1ny=1m[gcd(x,y)=i]

然后利用莫比乌斯反演我们马上就能够得到对于
F(i)=x=1ny=1m[i|gcd(x,y)]=[ni][mi]
f(i)=i|dμ(di)F(d)

那么就可以做了。
但是这只是第一步,我们马上又发现,这样时间空间好像都会爆……
但是我们又发现,由于 F(i) 里面写的是整除,所以实际上不同的值的个数只有 2(n+m) 个,于是可以分段存下相应的 F(d) ,然后根据乘法分配律,我们对于每一段相同 F(d) 计算的时候,只需要利用前缀和搞一搞莫比乌斯函数区间和就好了!
于是时间复杂度就是 O(nn) 了,代码也十分好写,只是要非常注意边界情况……
这题因为太水了我就不写 μ(d) 的线筛了,代码如下:

# include <algorithm>
# include <cstdio>
using namespace std;
const int maxn = 50010;
const int maxs = 50000;
int mu[maxn]={0,1};
bool nprime[maxn];
int prime[maxn],num;
int pre[maxn];
int k;
void getprime(){
    for (int i=2;i<=maxs;++i){
        if (!nprime[i]){ prime[num++]=i; mu[i]=-1; }
        for (int j=0;j<num&&prime[j]*i<=maxs;++j){
            nprime[i*prime[j]] = true;
            if (i%prime[j] == 0){
                mu[i*prime[j]] = 0;
                break;
            }
            else mu[i*prime[j]] = -mu[i];
        }
    }
}

int getnum(int x,int y){
    int last = 0;
    int ans = 0;
    x /= k; y /= k;
    for (int i=1;i<=min(x,y);i=last+1){
        last = min(x/(x/i),y/(y/i));
        ans += (pre[last]-pre[i-1])*(x/i)*(y/i);
    }   
    return ans;
}

int main(){
    getprime();
    for (int i=1;i<=maxs;++i) pre[i] = pre[i-1] + mu[i];
    int T; scanf("%d",&T);
    while (T--){
        int a,b,c,d;
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        --a; --c;   //Preffix
        printf("%d\n",getnum(b,d)-getnum(a,d)-getnum(c,b)+getnum(a,c));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值