【HAOI2011】【BZOJ2301】ProblemB

2301: [HAOI2011]Problem b
Time Limit: 50 Sec Memory Limit: 256 MB
Submit: 1756 Solved: 755
[Submit][Status][Discuss]
Description

对于给出的n个询问,每次求有多少个数对(x,y),满足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函数为x和y的最大公约数。

Input

第一行一个整数n,接下来n行每行五个整数,分别表示a、b、c、d、k

Output

共n行,每行一个整数表示满足要求的数对(x,y)的个数

Sample Input
2

2 5 1 5 1

1 5 1 5 2

Sample Output

14

3

HINT

100%的数据满足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000
学了反演先来写这个题233
既然在做之前已经知道了是反演,那就往反演那两个函数上想没的说。。。
先用容斥原理,拆分询问来应对下界a,c
设f(i)为 gcd(x,y)=i(1xn,1ym) 的数对数目,
和他对应的F(i)为 gcd(x,y)i
那么 f(i)=i|aμ(ai)F(a)=i|aμ(ai)nama
然后就得到了 O(n2) 的暴力反演
很显然过不了这个题。。。
怎么优化?
我们发现 nan 个取值
所以 nama 最多有
2(n+m) 个取值。
因此先对 μ 维护前缀和
然后在处理四个询问时候都只需要枚举 2(n+m) 个数而不需要像以前那样暴力枚举了
最后 O(nn) 过此题。

枚举除法的取值这种方法在莫比乌斯反演的应用当中非常常用,且代码并不难写//这是PoPoQQQ神犇在课件里的话。

要做好反演的题这种做法也是不可少的呢嗯。。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 51000
using namespace std;
int T;
int a,b,c,d,k;
bool not_prime[MAXN];
int num;
int prime[MAXN];
int mu[MAXN]={0,1};
int prev[MAXN];
int F[MAXN],f[MAXN];
void in(int &x)
{
    char ch=getchar();
    x=0;
    while (!(ch>='0'&&ch<='9')) ch=getchar();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void check_prime()
{
    for (int i=2;i<=50000;i++)
    {
        if(!not_prime[i])
            prime[++num]=i,mu[i]=-1;
        for (int j=1;j<=num&&prime[j]*i<=50000;j++)
        {
            not_prime[i*prime[j]]=1;
            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,f=0;
    x/=k;y/=k; for (int i=1;i<=min(x,y);i=last+1) { last=min(x/(x/i),y/(y/i)); f+=(prev[last]-prev[i-1])*(x/i)*(y/i);
    }
    return f;
}
int main()
{
    freopen("b.in","r",stdin);
    freopen("b.out","w",stdout);
    check_prime();
    for (int i=1;i<=50000;i++) prev[i]=prev[i-1]+mu[i];
    in(T);
    while (T--)
    {
        in(a);in(b);in(c);in(d);in(k);
        a--;c--;
        int ans=0;
        ans=getnum(b,d)-getnum(a,d)-getnum(c,b)+getnum(a,c);
        printf("%d\n",ans);
    }
}

你可能感兴趣的:(数论,莫比乌斯反演)