卧薪尝胆,厚积薄发。
HAOI2011 Problem b
Date: Fri Aug 10 22:32:38 CST 2018 In Category: NoCategory

Description:

对于给出的 $n$ 个询问,每次求有多少个数对 $(x,y)$ ,满足 $a\le x\le b$ , $c\le y\le d$ ,且 $gcd(x,y) = k$ 。
$1\le n \le 50000$

Solution:

先二维前缀和一下,变成 $\sum_{i=1}^n\sum_{j=1}^m[gcd(i,j)==k]$
也就是 $\sum_{i=1}^{\lfloor\frac n k\rfloor}\sum_{j=1}^{\lfloor \frac m k\rfloor}[gcd(i,j)==1]$
$\sum_{i=1}^{n }\sum_{j=1}^{m}[gcd(i,j)==1]=\sum_{i=1}^{n }\sum_{j=1}^{m}\varepsilon(gcd(i,j))$
反演得: $\sum_{i=1}^{n }\sum_{j=1}^{m}\sum_{d|i,d|j}\mu(d)$ ,
交换求和号得: $\sum_{d=1}^{min(n,m)}\sum_{i=1,d|i}^{n}\sum_{j=1,d|j}^{n}1=\sum_{d=1}^{min(n,m)}\lfloor \frac n d\rfloor\lfloor \frac m d\rfloor$
除法分块。
一定注意前缀和是除 $k$ 前的前缀和。

Code:


#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define MAXN 50010
typedef long long ll;
bool isprime[MAXN];
int prime[MAXN],tot = 0;
ll mu[MAXN];
ll sum[MAXN];
void sieve()
{
for(int i = 2;i < MAXN;++i)isprime[i] = true;
mu[1] = 1;sum[1] = 1;
for(int i = 2;i < MAXN;++i)
{
if(isprime[i])
{
prime[++tot] = i;
mu[i] = -1;
}
for(int j = 1;j <= tot && i * prime[j] < MAXN;++j)
{
isprime[i * prime[j]] = false;
if(i % prime[j] == 0)
{
mu[i * prime[j]] = 0;
break;
}
else
{
mu[i * prime[j]] = mu[i] * -1;
}
}
sum[i] = sum[i - 1] + mu[i];
}
return;
}
ll query(int n,int m)
{
if(n == 0 || m == 0)return 0;
ll res = 0;
for(int l = 1,r;l <= min(n,m);l = r + 1)
{
r = min(n / (n / l),m / (m / l));
res += (ll)(n / l) * (ll)(m / l) * (sum[r] - sum[l - 1]);
}
return res;
}
int main()
{
sieve();
int n;
scanf("%d",&n);
int a,b,c,d,k;
for(int i = 1;i <= n;++i)
{
scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
printf("%d\n",query(b / k,d / k) - query((a - 1) / k,d / k) - query(b / k,(c - 1) / k) + query((a - 1) / k,(c - 1) / k));
}
return 0;
}
Copyright © 2020 wjh15101051
ღゝ◡╹)ノ♡