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;
}
发布了95 篇原创文章 · 获赞 31 · 访问量 9万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览