215. 破译密码

题目链接

215. 破译密码

达达正在破解一段密码,他需要回答很多类似的问题:

对于给定的整数 \(a,b\)\(d\),有多少正整数对 \(x,y\),满足 \(x \le a,y \le b\),并且 \(gcd(x,y)=d\)

作为达达的同学,达达希望得到你的帮助。

输入格式

第一行包含一个正整数 \(n\),表示一共有 \(n\) 组询问。

接下来 \(n\) 行,每行表示一个询问,每行三个正整数,分别为 \(a,b,d\)

输出格式

对于每组询问,输出一个正整数,表示满足条件的整数对数。

数据范围

\(1 \le n \le 50000\),
\(1 \le d \le a,b \le 50000\)

输入样例:

2
4 5 2
6 4 3

输出样例:

3
2

提示:\(gcd(x,y)\) 返回 \(x,y\) 的最大公约数。

解题思路

容斥原理,莫比乌斯函数

莫比乌斯函数:\(mobius[i]\)\(mobius[i]\),对 \(i\) 分解质因数,若 \(i\) 某一项质因子个数大于 \(1\),则 \(mobius[i]=0\),如果质因子种数为奇数则 \(mobius[i]=-1\),否则 \(mobius[i]=1\)理解:对于 \(n\) 的欧拉函数,由容斥原理,得 $\phi(n)=n-s_2-s_3-\dots + s_{2,3}+s_{3,5}+\dots - s_{2,3,5}-\dots +\dots $,其中 \(s_{a_1,a_2,\dots a_m}\) 表示与 \(n\) 的公因子为 \(a_1,a_2,\dots a_m\) 的个数,其系数正好为莫比乌斯函数

由于 \(gcd(x,y)=d\),即 \(gcd(x/d,y/d)=1\),问题转换为满足 \(x\in[1,a/d],y\in[1,b/d]\)\(gcd(x,y)=1\) 的对数,由容斥原理,答案即为 \((a/d)\times (b/d)-s_2-s_3-\dots +s_6+\dots -\dots\),令 \(a=a/d,b=b/d\),答案即为 \(a\times b-(a/2)\times (b/2)-(a/3)\times (b/3)+(a/6)\times (b/6)+\dots - \dots\),令 \(n=min(a,b)\),即为 \(\sum_{i=1}^{n}(a/i)\times (b/i)\times mobius[i]\),其中 \(a/i\) 共有 \(O(\sqrt{a})\) 种取值,简略说下,将 \(a\) 分成两段:\([1,\sqrt{a}],[\sqrt{a},a]\)\(a/i\) 分成的两段的值的范围都为 \(O(\sqrt{a})\)
另外还有如下定理:
对于 \(a/x\) 该段取值的最大的一个 \(x\)\(g(x)=a/(a/x)\)\(a,b\) 表示的段都是 \(O(根号)\) 级别,每次跳到 \(a,b\) 表示的段的较小边界即可

  • 时间复杂度:\(O(n\sqrt{n})\)

代码

// Problem: 破译密码
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/217/
// Memory Limit: 64 MB
// Time Limit: 2000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=5e4+5;
LL res;
int prime[N],u[N],s[N],v[N],m;
void primes(int n)
{
	u[1]=1;
	for(int i=2;i<=n;i++)
	{
		if(v[i]==0)
		{
			u[i]=-1;
			v[i]=prime[++m]=i;
		}
		for(int j=1;prime[j]*i<=n&&j<=m;j++)
		{
			if(v[i]<prime[j])break;
			if(i%prime[j]==0)
				u[i*prime[j]]=0;
			else
				u[i*prime[j]]=-u[i];
			v[i*prime[j]]=prime[j];
			
		}
	}
	for(int i=1;i<=n;i++)s[i]=s[i-1]+u[i];
}
int g(int a,int b)
{
	return a/(a/b);
}
int main()
{
    primes(N-1);
    int t;
    cin>>t;
    while(t--)
    {
    	int a,b,d;
    	cin>>a>>b>>d;
    	a/=d,b/=d;
    	int n=min(a,b);
    	res=0;
		for(int l=1,r;l<=n;l=r+1)
		{
			r=min(g(a,l),g(b,l));
			res+=1ll*(s[r]-s[l-1])*(a/l)*(b/l);
		}
		cout<<res<<'\n';
    } 
    return 0;
}
posted @ 2022-06-28 21:26  zyy2001  阅读(66)  评论(0编辑  收藏  举报