P7696 [COCI2009-2010#4] IKS题解

文章介绍了COCI2009-2010#4的数学问题,要求计算经过特定操作后序列的最大公因数及最少操作次数。解题策略是分解序列中每个数字的质因数,找出“有用”的质数,平均分配给每个元素以求最大公因数,并计算每个质因子低于平均值的次数作为操作次数。
摘要由CSDN通过智能技术生成

[COCI2009-2010#4] IKS

题目背景

Mirko 的伟大的曾祖母 Katica 是一位狂热的数学家,她喜欢用数学游戏折磨她的曾孙。

题目描述

这一天,她在一张纸上写下了一个 n n n 个数字的序列,并告知 Mirko 可以做如下操作若干次:

  • 选取序列中的两个数字(我们不妨称之为 A , B A,B A,B),然后选取能整除 A A A 的质数 X X X。之后,Mirko 把 A A A 擦去并在原来的位置上写下 A X \frac AX XA,然后他把 B B B 擦去并在原来的位置上写下 B × X B\times X B×X

Mirko 希望得到最大分数,因为这样他可以从他的曾祖母那里得到糖果。一个包含 n n n 个数字的序列的分数是 n n n 个数字的最大公因数

Mirko 并不擅长这个,但又很想希望得到糖果,因此找到了你,希望你能编写一个程序计算最大的可能分数,同时他也希望你能够得出在得到最大可能的分数的前提下最少应当执行的操作数。

输入格式

输入共两行。

第一行一个整数 n n n,代表序列中的元素个数。
第二行包含 n n n 个整数,描述 Katica 给 Mirko 的序列。

输出格式

输出仅一行两个整数,第一个整数表示 Mirko 最大能够得到的分数,第二个整数表示 Mirko 在能够获得最大的分数的前提下最少应当执行的操作数。

样例 #1

样例输入 #1

3
4 4 1

样例输出 #1

2 1

样例 #2

样例输入 #2

3
8 24 9

样例输出 #2

12 3

样例 #3

样例输入 #3

5
4 5 6 7 8

样例输出 #3

2 2

提示

【样例 1 解释】

对于样例 1 1 1,Mirko 可以选择序列中的 4 4 4 1 1 1 两个数,并选择 4 4 4 的唯一质因子 2 2 2。之后 Mirko 将 4 4 4 1 1 1 擦去,并在它们原来所在的位置上都写下了 2 2 2,这样这个序列的分数是 2 2 2。可以证明这样的方案可以获得最大的分数并且在此前提下操作数最少。

【数据范围】

对于所有数据, 1 ⩽ n ⩽ 100 1\leqslant n\leqslant 100 1n100,序列中的元素不超过 1 0 6 10^6 106

题解部分

题目传送门

看到别的题解都要素数筛,,我觉得没必要(雾 ,毕竟我们只需要 “有用” “有用” 有用的质数,于是我来发一篇题解

分析题意

本题要求处理一串序列,要求使得处理后的序列公因数最大
处理要求:选两个数AB,A除以X,B乘以X(X为能整除A的质数)

题解(代码部分有详细注释,建议搭配代码食用)

既然要求公因数最大,那么我们最好让数列中每个元素含有相同的质因数

将每个元素分解质因数后,记录每个质因子出现的次数,除以n得到每个元素获得该质因子的次数,表示将该质因子平均分配给每个元素,这样这个序列就有共同的质因子力!ans累乘即可。

tips:在分解质因数的过程中,我们可以顺带记录 “有用” “有用” 有用的质因子,详情请见代码部分

关于最小操作次数,只需要遍历序列,查找序列中每个元素 所对应的 质因子的重数(即该质因子被分解的次数),如果低于平均值,将该平均值减去重数的值累加到sum即可

看不懂也没关系,还有最后一层保护网:code+注释

code:

#include <iostream>  //6
using namespace std;
const int N=1e6+7;
int n,tot[N];  // tot[i]表示质因子i被该数列中所有元素分解出来的总次数 
int is[105],init,bj[N];  // is[]存储被分解出来的质因子 
struct T{
	int a,d[N];  // d[i]表示该元素的质因子i配分解出来的次数 
	inline void divide(int a){
		for(int i=2;i*i<=a;i++){  //只需要分解到sqrt(n),剩下的a肯定是质数(但我不会证明qwq 
			if(a%i!=0) continue;  //6
			if(!bj[i]){  //bj…顾名思义…标记数组 
				init++;  // init记录分解出质因子的个数 
				is[init]=i;  // 将该质因子存入数组中 
				bj[i]=1;  // 将该质因子标记,防止重复存储 
			}
			while(a%i==0){  // 分解该质因子,直至无法分解 
				d[i]++;  // 每分解一次,该质因子的重数++ (重数:即被分解出来的次数) 
				tot[i]++;  // 该质因子的总重数++ 
				a/=i;  // 分解 
			}
		}if(a>1){  // 如果没分解完,剩下的肯定是a的质因子 ,接下来的操作和上述原理相同 
			if(!bj[a]){  // 质因子是否存储过 
				init++; 
				is[init]=a;  //C
				bj[a]=1;
			}
			d[a]++;
			tot[a]++;
		}
		return ;  // C
	}
}t[105];  // 记录元素 
int ans=1,sum;   // ans最大公因数,sum最小操作次数 
int main(){
	cin>>n;  // 100个数据也不用快读吧…… 
	for(int i=1;i<=n;i++){
		cin>>t[i].a;  // F
		t[i].divide(t[i].a);  // 分解该元素 
	}
	for(int i=1;i<=init;i++){  // 遍历所有被分解出来的质因子 
		int x=tot[is[i]]/n;  // 平均分配每个质因子 ,并向下取整 
		if(x==0) continue;  // 如果无法平均分配 continue 
		for(int j=1;j<=x;j++){
			ans*=is[i];  // 累乘最大公因数 
		}
		for(int j=1;j<=n;j++){  // 遍历每个元素 
			if(x>t[j].d[is[i]]){  // 如果当前元素的当前质因子的重数小于平均值 ,最小次数++ 
				sum+=x-t[j].d[is[i]];  //F
			}
		}
	}
	cout<<ans<<" "<<sum;  // 卡哇伊! 
	return 0;
}

求个点赞不过分吧~qql

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值