四元组

题面

题目描述

今天藤藤闲来无事,想到了一个奇怪的问题,但这个问题把它自己难住了,需要你来帮帮他。
一个长度为 n 的序列,从这序列里面选4个数,要求这四个数的 gcd 为1,不要求它们两两互质。

定义:gcd 即为数学意义上的最大公约数

假设四个数为a,b,c,d 那么四个数的最大公约数被定义为gcd(a,gcd(b,gcd(c,d))),

此处函数 gcd 为求两个数的最大公约数

你需要帮助藤藤找到这个序列里一共有多少个满足条件的四元组

输入格式

第一行为整数n,序列元素的个数。
第二行有n个数ai,表示序列里面的元素(存在多个元素相等)

输出格式

输出满足条件的四元组数量

输入输出样例

输入 #1复制

4
2 3 4 5

输出 #1复制

1

输入 #2复制

4
2 4 6 8

输出 #2复制

0

输入 #3复制

7
2 3 4 5 7 6 8

输出 #3复制

34

说明/提示

样例解释:
样例1:2345一个满足条件的四元组
样例2:没有满足条件的四元组
样例3:除了2468,其余都满足
对于10%的数据,满足1≤n≤10,1≤ai≤100
对于30%的数据,满足1≤n≤50,1≤ai≤10000
对于100% 的数据,满足1≤n≤10000,1≤ai≤10000

解答

30分解法

既然说是四个数的 gcd 为 1,不妨开四重 for 循环解答。

注意每重都要从上一重加 1 开始循环,最后判断就行。

时间复杂度 O(n^4) 。对于 n 小于 50 是不会 TLE 的。

判断 gcd 可以写函数

int gcd(int n,int m){
	if(m==0)return n;
	return gcd(m,n%m);
}

当然,万能头里也有自带函数 __gcd,很方便。

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e4+5;
ll n,a[N],ans;
int main(){
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<n-2;i++)
		for(int j=i+1;j<n-1;j++)
			for(int k=j+1;k<n;k++)
			    for(int l=k+1;l<=n;l++)
					if(__gcd(a[i],__gcd(a[j],__gcd(a[k],a[l])))==1)
						ans++;
	cout<<ans;
	return 0;
}

很好,30分。

满分做法

我们知道如果两数的最大公约数为 1,说明它们俩的所有因数只有 1 相同。对,先分解每个数的因数!我们用 dp 数组记录所有有 i 因数的数的个数。……额,可能有些绕口,那就在代码中理解。

void init(ll x){
	for(ll i=1;i*i<=x;i++){
		if(x%i==0){
			dp[i]++;
			if(x/i!=i)dp[x/i]++;
			maxn=max(maxn,x/i);
		}
	}
}

maxn 用来记录因数的最大值,以便后面循环。当然,x / i 是大于等于 i 的。

当前时间复杂度 O(√ ̄n),加上循环 a [ i ] 时间复杂度 O(n√ ̄n);

接着就要开始加工了,首先要去掉那些不满四个的因数,还原回 0。其他的加工成  C ( i , 4 ),表示共有多少种不同的组合法,可以简单写成

dp[i]=dp[i]*(dp[i]-1)*(dp[i]-2)*(dp[i]-3)/24;

C表示组合数,没学过的可以百度搜一下。

然后,我们要除去一些重复的因数,比如因数 3 的数量包含了因数 6 的数量,所以要减去因数 6 的数量,可以每次让 i 乘 2 去找因数 i 的倍数,如果有且没越界,就减去。

注意循环要从 maxn 开始反着循环到 1,这样减去时就不会重复;

最后输出因数为 1 的数量,也就是 dp[1] 就行了。

AC主函数代码

(预处理前面已经公布)

int main(){
//	freopen("quaternion.in","r",stdin);
//	freopen("quaternion.out","w",stdout);
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		init(a[i]);
	}
	for(int i=1;i<=maxn;i++)
		if(dp[i]>=4)
			dp[i]=dp[i]*(dp[i]-1)*(dp[i]-2)*(dp[i]-3)/24;
		else dp[i]=0;
	for(int i=maxn;i>=1;i--){
		if(dp[i]){
			int j=i+i;
			while(j<=maxn){
				dp[i]-=dp[j];
				j+=i;
			}
		}
	}
	cout<<dp[1];
	return 0;
} 

我查了一下,这道我们团队比赛的题目在洛谷上有 原题,竟是 紫题!不过,我把这个代码稍微改一下(变成多组数据),还是AC,并不难。

又来一次完美撒花!

 认为讲得好记得关注我 wangkeliniii  谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值