triple
Time Limit:3000MS
Memory Limit:65536KB
Description
给出一个整数n,表示1,2,...,n。从这n个数中任意选择3个不同的数字x,y,z,问x,y,z的最大公约数等于m的方案有多少种?(注意:(1,2,3),(1,3,2),(2,1,3),(2,3,1),(3,1,2),(3,2,1)属于同一种方案)
Input
第一行输入一个整数T(1 <= T <= 100),表示有T组数据,接下来T行,每行2个整数n, m(1 <= m <= n <= 10^5)
Output
输出一个整数表示答案
Sample Input
1
5 1
Sample Output
10
题目和提交链接:点击打开链接
思路:求gcd(x,y,z)==m的方案数,那么x,y,z肯定是m的倍数,所有只考虑倍数就可以了。倍数的个数d=n/m,其实就可以转化为求1~d中三个不同的数的gcd==1 的情况。那么方案数 = 总情况数(C(d,3) ) - gcd=(2,3,4......d)的情况数。dp[a]表示gcd==a的情况,1~d中a的倍数有b=(d/a)个,dp[a]=C(b,3) - dp[ 2a,3a,4a...] 。
详细见代码:
//复杂度接近于O(n)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
typedef unsigned long long LLu;
typedef long long LL;
const int maxn=1e5+100;
LL dp[maxn];
int A[maxn];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,m;
int i;
memset(dp,0,sizeof dp);
scanf("%d%d",&n,&m);
n=n/m;
LL ans=(LL)n*(n-1)*(n-2)/6;
for(i=n;i>=2;i--)//一开始循环方向写反了浪费了一对堆时间
{
int a=n/i;
dp[i]+=(LL)a*(a-1)*(a-2)/6;
int b=i;
b+=i;
while(b<=n)
{
dp[i]-=dp[b];
b+=i;
}
}
for(i=2;i<=n;i++)
{
ans-=dp[i];
}
cout<<ans<<endl;
}
return 0;
}