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

题目链接

题意为求出 T T T i = a b i = c d [ g c d ( i , j ) = k ] \sum\limits_{i=a}^{b}\sum\limits_{i=c}^{d}[gcd(i,j)=k] i=abi=cd[gcd(i,j)=k]

根据容斥原理可以将算式转化为

<munderover> i = 1 b </munderover> <munderover> j = 1 d </munderover> [ g c d ( i , j ) = k ] <munderover> i = 1 b </munderover> <munderover> j = 1 c 1 </munderover> [ g c d ( i , j ) = k ] <munderover> i = 1 a 1 </munderover> <munderover> j = 1 d </munderover> [ g c d ( i , j ) = k ] + <munderover> i = 1 a 1 </munderover> <munderover> j = 1 c 1 </munderover> [ g c d ( i , j ) = k ] \sum\limits_{i=1}^{b}\sum\limits_{j=1}^{d}[gcd(i,j)=k]-\sum\limits_{i=1}^{b}\sum\limits_{j=1}^{c-1}[gcd(i,j)=k]-\sum\limits_{i=1}^{a-1}\sum\limits_{j=1}^{d}[gcd(i,j)=k]+\sum\limits_{i=1}^{a-1}\sum\limits_{j=1}^{c-1}[gcd(i,j)=k] i=1bj=1d[gcd(i,j)=k]i=1bj=1c1[gcd(i,j)=k]i=1a1j=1d[gcd(i,j)=k]+i=1a1j=1c1[gcd(i,j)=k]

其中每一块算式都为

<munderover> i = 1 n </munderover> <munderover> j = 1 m </munderover> [ g c d ( i , j ) = k ] ( n &lt; m ) \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(i,j)=k] (n&lt;m) i=1nj=1m[gcd(i,j)=k](n<m)

化简该式

<munderover> i = 1 n k </munderover> <munderover> j = 1 m k </munderover> [ g c d ( i , j ) = 1 ] \sum\limits_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{k} \rfloor}[gcd(i,j)=1] i=1knj=1km[gcd(i,j)=1]

利用莫比乌斯函数性质可得

<munderover> i = 1 n k </munderover> <munderover> j = 1 m k </munderover> <munder> d g c d ( i , j ) </munder> μ ( d ) \sum\limits_{i=1}^{\lfloor \frac{n}{k} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{k} \rfloor}\sum\limits_{d|gcd(i,j)}\mu(d) i=1knj=1kmdgcd(i,j)μ(d)

因为 d g c d ( i , j ) d|gcd(i,j) dgcd(i,j) 所以 d i d|i di d j d|j dj ,考虑枚举d得

<munderover> d = 1 n k </munderover> μ ( d ) <munderover> i = 1 n k </munderover> [ d i ] <munderover> j = 1 m k </munderover> [ d j ] \sum\limits_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d) \sum\limits_{i=1}^{\lfloor \frac{n}{k} \rfloor}[d|i] \sum\limits_{j=1}^{\lfloor \frac{m}{k} \rfloor}[d|j] d=1knμ(d)i=1kn[di]j=1km[dj]

其中 i = 1 n k [ d i ] \sum\limits_{i=1}^{\lfloor \frac{n}{k} \rfloor}[d|i] i=1kn[di] [ 1 , n k ] [1, \lfloor \frac{n}{k} \rfloor ] [1,kn] d d d 的倍数, j = 1 m k [ d j ] \sum\limits_{j=1}^{\lfloor \frac{m}{k} \rfloor}[d|j] j=1km[dj] [ 1 , m k ] [1, \lfloor \frac{m}{k} \rfloor ] [1,km] d d d 的倍数,两项可化简为 n k d \lfloor \frac{n}{kd} \rfloor kdn m k d \lfloor \frac{m}{kd} \rfloor kdm

这样就可推导出 i = 1 n j = 1 m [ g c d ( i , j ) = k ] = d = 1 n k μ ( d ) n k d m k d ( n &lt; m ) \sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(i,j)=k]=\sum\limits_{d=1}^{\lfloor \frac{n}{k} \rfloor}\mu(d) \lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd} \rfloor (n&lt;m) i=1nj=1m[gcd(i,j)=k]=d=1knμ(d)kdnkdm(n<m)

其中莫比乌斯函数 μ \mu μ 使用线性筛筛出,这样算式就可以用一个函数计算( N N N M M M 传参之前已除 K K K

int Solve(int N, int M) {
    if (N > M) swap(N, M);
    int Res = 0;
    for (int d = 1; d <= N; ++d) {
        Res += Mu[d] * (N / d) * (M / d);
    }
    return Res;
}

但是这道题目有 T T T 组询问,而我们每次四个算式的计算都会有 O ( n ) O(n) O(n) 的复杂度,这样会导致超时

所以需要用到分块来进行优化将代码改为这样

int Solve(int N, int M) {
	if (N > M) swap(N, M);
	int Res = 0;
	for (int d = 1, tp; d <= N; d = tp + 1) {
		tp = min(N / (N / d), M / (M / d));
		Res += (PrefixMu[tp] - PrefixMu[d - 1]) * (N / d) * (M / d);
	}
	return Res;
}

这样每次计算的时间复杂度就为 O ( n ) O(\sqrt{n}) O(n )

分块的原理为对于一些 i i i n i \lfloor \frac{n}{i} \rfloor in 的值是相等的(因为下取整),所以可以将其合并以求 i = 1 n n i \sum\limits_{i=1}^{n} \lfloor \frac{n}{i} \rfloor i=1nin 那么 n k d m k d \lfloor \frac{n}{kd} \rfloor \lfloor \frac{m}{kd} \rfloor kdnkdm 部分便可以使用分块优化,而在分块优化的同时另一部分 μ ( d ) \mu(d) μ(d) 需要使用前缀和预处理

分块上界的计算小技巧为 n n l \lfloor \frac{n}{\lfloor \frac{n}{l} \rfloor} \rfloor lnn

AC代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = (int)(5e4 + 5);

bool IsPrime[maxn];
int Tot;
int Prime[maxn];
int Mu[maxn];
int PrefixMu[maxn];

void Moblus() {
	for (int i = 0; i < maxn; ++i) IsPrime[i] = true;
	Mu[1] = 1;
	for (int i = 2; i < maxn; ++i) {
		if (IsPrime[i]) {
			Prime[Tot++] = i;
			Mu[i] = -1;
		}
		for (int j = 0; j < Tot && Prime[j] * i < maxn; ++j) {
			IsPrime[i * Prime[j]] = false;
			if (i % Prime[j] == 0) {
				Mu[i * Prime[j]] = 0;
				break;
			}
			Mu[i * Prime[j]] = -Mu[i];
		}
	}
	for (int i = 1; i < maxn; ++i) PrefixMu[i] = PrefixMu[i - 1] + Mu[i];
}

int T;
int A, B, C, D, K;
int Ans;

int Solve(int N, int M) {
	if (N > M) swap(N, M);
	int Res = 0;
	for (int d = 1, tp; d <= N; d = tp + 1) {
		tp = min(N / (N / d), M / (M / d));
		Res += (PrefixMu[tp] - PrefixMu[d - 1]) * (N / d) * (M / d);
	}
	return Res;
}

int main(int argc, char *argv[]) {
	Moblus();
	scanf("%d", &T);
	for (int Case = 1; Case <= T; ++Case) {
		scanf("%d%d%d%d%d", &A, &B, &C, &D, &K);
		Ans = 0;
		Ans += Solve(B / K, D / K);
		Ans -= Solve((B / K), (C - 1) / K);
		Ans -= Solve((A - 1) / K, D / K);
		Ans += Solve((A - 1) / K, (C - 1) / K);
		printf("%d\n", Ans);
	}
	return 0;
}
全部评论

相关推荐

10-11 17:30
湖南大学 C++
我已成为0offer的糕手:羡慕
点赞 评论 收藏
分享
一个菜鸡罢了:哥们,感觉你的简历还是有点问题的,我提几点建议,看看能不能提供一点帮助 1. ”新余学院“别加粗,课程不清楚是否有必要写,感觉版面不如拿来写一下做过的事情,教育经历是你的弱势就尽量少写 2. “干部及社团经历”和“自我评价”删掉 3. 论文后面的“录用”和“小修”啥的都删掉,默认全录用,问了再说,反正小修毕业前肯定能发出来 4. 工作经验和研究成果没有体现你的个人贡献,着重包装一下个人贡献
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务