【刷题】BZOJ 2301 [HAOI2011]Problem b

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

Solution

这一题也是莫比乌斯反演
式子和上一篇的一模一样
所以接下来的\(solve\)怎么求可以看上篇和上上篇,这里只列出一个式子
\[ solve(n,m,k)=f(k)=\sum_{T=1}^{min(n,m)}\mu(\lfloor \frac{T}{k} \rfloor)\lfloor \frac{n}{T} \rfloor \lfloor \frac{m}{T} \rfloor \]
\(\mu\)作线性筛和前缀和,枚举作整除分块

这题因为有下界,所以我们需要容斥一下
\[ ans=solve(b,d,k)-solve(a-1,d,k)-solve(b,c-1,k)+solve(a-1,c-1,k) \]
这里的\(solve(n,m,k)\)就是求满足\(1 \le x \le n\)\(1 \le y \le m\)时,有多少数对\((x,y)\)的gcd等于k
感性上,就是 \(||\) 1到b和1到d的整段 \(||\) 先减去 \(||\) 1到a-1和1到d的整段 \(||\) ,这样就去除了1到a-1段中的贡献;同理,再减去1到c-1段中的贡献,这样多算的就去掉了,但发现
\(||\) 1到a-1和1到c-1的整段 \(||\) 减了两次,所以又要加上来
但这样做还是是会超时
我们又发现\(solve(n,m,k)=solve(n/k,m/k,1)\)
因为在n/k和m/k中的gcd为1的数对同时乘以k,那么他们的gcd就变成了k,而且也不会超上界
那么我们的ans就可以变为
\[ ans=solve'(b/k,d/k)-solve'((a-1)/k,d/k)-solve'(b/k,(c-1)/k)+solve'((a-1)/k,(c-1)/k) \]
\((solve'(n,m)=solve(n,m,1))\)

#include<bits/stdc++.h>
#define ll long long
const int MAXN=50000+1;
int T,prime[MAXN],cnt,mu[MAXN],s[MAXN];
bool vis[MAXN];
template<typename T> inline void read(T &x)
{
    T data=0,w=1;
    char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    x=data*w;
}
template<typename T> inline void write(T x,char c='\0')
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
    if(c!='\0')putchar(c);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void init()
{
    memset(vis,1,sizeof(vis));
    vis[0]=vis[1]=0;
    mu[1]=1;
    for(register int i=2;i<MAXN;++i)
    {
        if(vis[i])
        {
            prime[++cnt]=i;
            mu[i]=-1;
        }
        for(register int j=1;j<=cnt&&i*prime[j]<MAXN;++j)
        {
            vis[i*prime[j]]=0;
            if(i%prime[j])mu[i*prime[j]]=-mu[i];
            else break;
        }
    }
    for(register int i=1;i<MAXN;++i)s[i]=s[i-1]+mu[i];
}
inline ll solve(int a,int b)
{
    ll res=0;
    for(register int i=1;;)
    {
        if(i>min(a,b))break;
        int j=min(a/(a/i),b/(b/i));
        res+=(ll)(a/i)*(ll)(b/i)*(ll)(s[j]-s[i-1]);
        i=j+1;
    }
    return res;
}
int main()
{
    init();
    read(T);
    while(T--)
    {
        int a,b,c,d,k;
        read(a);read(b);read(c);read(d);read(k);
        write(solve(b/k,d/k)-solve((a-1)/k,d/k)-solve(b/k,(c-1)/k)+solve((a-1)/k,(c-1)/k),'\n');
    }
    return 0;
}
posted @ 2018-03-09 19:29  HYJ_cnyali  阅读(...)  评论(...编辑  收藏