【HAOI2011】【BZOJ2301】ProblemB

来源:互联网 发布:刺马真相知乎 编辑:程序博客网 时间:2024/06/06 01:47

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 51000using 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);    }}
1 0