【codechef】Gcd Queries(灵活题,分类)

#include<iostream>
#include<algorithm>
#include<string>
#include<map>
#include<string.h>
#include<vector>
#include<cmath>
#include<stdlib.h>
#include<cstdio>
#define ll long long
using namespace std;
int x[100001]; //把memset删掉就不超时了。。。
int y[100001];
int gcd(int a,int b){
        return b?gcd(b,a%b):a;}
int main(){
	int t,a,b;
	scanf("%d",&t);
	while(t--){
		int n,m;
		scanf("%d%d",&n,&m); 
		scanf("%d",&y[0]);
		x[0]=y[0];
		for(int i=1;i<n;++i){
			scanf("%d",&y[i]);
			x[i]=gcd(x[i-1],y[i]);  //从前往后的gcd
		}
		for(int i=n-2;i>=0;--i){
			y[i]=gcd(y[i+1],y[i]);  //从后往前的gcd
		}
		while(m--){
			scanf("%d%d",&a,&b); 
			if(a==1)
				printf("%d\n",y[b]); //分类
			else if(b==n)
				printf("%d\n",x[a-2]);  //分类
			else
			printf("%d\n",gcd(y[b],x[a-2]));  //分类
		}
	}
	return 0;
}


<think>好的,用户现在需要解决的问是,给定一个数组,通过任意次操作(取两个数,用大的减去小的替换大的),求操作后的最小数组和。并且用户特别指出需要自己编写gcd函数,而不是使用标准库的std::gcd。我需要先回顾之前的思路,然后调整代码,自己实现gcd函数。 首先,问的核心在于,无论操作多少次,数组元素的最大公约数(GCD)保持不变。最终的最小和就是所有元素的GCD。因为每次操作相当于在两个数之间进行减法,类似于欧几里得算法求GCD的过程,所以整个数组的GCD不会改变。最终数组的最小可能和就是这个GCD,因为可以通过操作将所有数都变为GCD或者0,而GCD是最小的非零数,所以和为GCD(如果所有数都是0的话和为0)。 现在用户要求自己实现gcd函数。标准的gcd实现通常使用欧几里得算法,即gcd(a, b) = gcd(b, a % b),直到b为0时返回a。需要注意处理负数的情况,因为目中的数组元素可能是负数,但gcd是取绝对值后的公约数。所以在计算时,应该先取绝对值。 接下来,我需要编写一个自己的gcd函数。函数的输入是两个整数,输出是它们的最大公约数。需要处理其中一个数为0的情况吗?比如,如果其中一个数是0,那么gcd是另一个数的绝对值。例如,gcd(0, a) = |a|,gcd(a, 0) = |a|。如果两个都是0,通常认为gcd是0。 然后,在代码中,首先读取数组,计算所有元素的绝对值的gcd。初始时,current_gcd是第一个元素的绝对值。然后遍历数组中的每个元素,依次计算current_gcd和当前元素绝对值的gcd,更新current_gcd。最后输出current_gcd,这就是操作后的最小和。 需要注意数组长度为0的情况,但目可能保证数组非空。如果数组长度为1,那么无法进行操作,所以和就是该元素本身的绝对值?或者原数?比如数组是[5],无法操作,所以和是5。但根据之前的分析,当数组长度为1时,无法进行操作,所以最小和就是原数的绝对值吗?或者原数?比如原数是-5,绝对值是5,但原数的和是-5?但目中的操作是替换大的数,当只有一个数时,无法操作,所以和就是原数的和。但原问中的数组和是指元素的和,所以如果数组只有一个元素,比如-5,和就是-5。但根据之前的结论,数组的gcd是该数的绝对值,而最小和应该是该gcd吗?这里可能需要再仔细考虑。 不过根据问描述,操作是任意次,当数组长度为1时,无法进行任何操作,所以和就是原数的和。但根据之前的分析,数组的gcd是该数的绝对值,而当数组长度为1时,gcd是该数的绝对值。例如数组是[5],gcd是5,和是5;数组是[-5],gcd是5,和是-5?这时候似乎矛盾。可能我之前的结论需要修正。 哦,这里可能我之前的分析有误。当数组长度为1时,无法进行操作,所以和就是原数本身。而如果数组长度大于等于2,那么通过操作可以将数组中的数逐步减少到它们的gcd。例如,数组是[5, 10],gcd是5。操作后可以变成[5,5](10-5=5替换10),和为10;或者进一步操作,5和5相减得到0,替换其中一个5,得到[0,5],和为5;再操作一次,5-0=5,替换5,得到[0,5],和还是5?或者可以继续操作吗?比如0和5,大的数是5,减去0得到5,替换后还是0和5,所以无法再减少。这时候和为5,即gcd。所以当数组长度>=2时,最小和是gcd。当数组长度为1时,和就是原数本身。但原问中的数组可能包含负数吗?比如数组是[-5],和是-5,而gcd是5。但根据操作的定义,操作只能在两个数之间进行,所以当数组长度为1时,无法操作,和就是原数的和。所以在代码中,如果数组长度为1,输出原数的绝对值吗?或者原数? 可能我之前的结论正确的前提是数组长度>=2。需要再仔细分析。例如,数组是[3],无法操作,和是3。而gcd是3,所以此时和等于gcd。如果数组是[-3],和是-3,而gcd是3。这时候和与gcd不一致。但问中的操作是否允许处理负数?例如,取两个数,大的减去小的。比如-3和-5,大的数是-3(因为-3 > -5),所以用-3 - (-5) = 2替换大的数-3,得到数组[2, -5]。此时和是-3,而原来的和是-8。所以操作可能改变和,但最终的最小和应该等于数组元素的gcd的绝对值吗? 或者,可能我之前的结论正确,即无论数组元素是否为负数,最终的最小和等于所有元素的绝对值的gcd。例如,数组是[-5, -10],绝对值的gcd是5。操作后可以得到5和0,和为5。而原数组的和是-15,操作后的最小和是5,等于gcd。 所以回到问,当数组长度为1时,无法进行操作,所以和就是原数本身。但原数的绝对值的gcd是它的绝对值。例如数组是[-5],和是-5,而绝对值的gcd是5。这时候可能我的之前的结论需要修正:当数组长度>=2时,最小和是绝对值的gcd;当数组长度为1时,和是原数本身。但原问中是否可能数组长度为1?目中没有说明,但通常编程可能假设数组长度>=1。 现在回到用户的问,用户要求自己编写gcd函数。所以需要实现一个计算两个数的最大公约数的函数,处理负数的情况,取绝对值后计算。 编写gcd函数的步骤: 函数名可以是my_gcd,参数是两个整数a和b。首先将a和b取绝对值,然后应用欧几里得算法。 例如: int my_gcd(int a, int b) { a = abs(a); b = abs(b); while (b != 0) { int temp = a % b; a = b; b = temp; } return a; } 这样,当b为0时,返回a的绝对值(因为a已经取过绝对值了)。例如,my_gcd(0, 5)返回5;my_gcd(5, 0)返回5;my_gcd(0,0)返回0。 然后,在主函数中,计算数组所有元素的绝对值的gcd。初始current_gcd是第一个元素的绝对值。然后遍历数组的每个元素,依次计算current_gcd和当前元素的绝对值的gcd,更新current_gcd。 例如,数组是[12, 18, 24],绝对值的gcd是6。所以操作后的最小和是6。 数组是[5, 10],gcd是5,操作后的最小和是5(如5和5,和为10?或者可以变成0和5,和为5?哦,对,之前的分析可能有误。比如,5和10,第一次操作10-5=5,得到[5,5],和为10。第二次操作,取两个5,大的减去小的是0,替换其中一个5,得到[0,5],和为5。此时无法再操作,因为0和5相减还是5,替换后还是0和5。所以和为5,即gcd。所以之前的结论正确,最终的最小和是gcd。 所以代码的逻辑是正确的。现在需要将std::gcd替换为自己实现的my_gcd函数。 现在编写完整的C++代码: 首先包含必要的头文件,如<iostream>, <vector>, <cmath>(用于abs函数)。 然后实现my_gcd函数。 主函数中读取数组,处理空数组的情况(如果有的话),然后计算所有元素的gcd。 例如: #include <iostream> #include <vector> #include <cmath> // 用于abs函数 using namespace std; int my_gcd(int a, int b) { a = abs(a); b = abs(b); while (b != 0) { int temp = a % b; a = b; b = temp; } return a; } int main() { int n; cin >> n; vector<int> nums(n); for (int i = 0; i < n; ++i) { cin >> nums[i]; } if (n == 0) { cout << 0 << endl; return 0; } int current_gcd = abs(nums[0]); for (int i = 1; i < n; ++i) { current_gcd = my_gcd(current_gcd, nums[i]); if (current_gcd == 0) { break; // 所有数都是0,gcd为0 } } cout << current_gcd << endl; return 0; } 这样,当数组中的元素都是0时,current_gcd会是0,输出0。例如,数组是[0,0,0],输出0。 测试案例: 案例1:输入数组[12, 18, 24],gcd是6,输出6。 案例2:输入数组[5, 10],gcd是5,输出5。 案例3:输入数组[-5, -10],绝对值的gcd是5,输出5。 案例4:输入数组[0, 0],gcd是0,输出0。 案例5:输入数组[7],输出7(因为无法操作,和为7,而gcd是7)。 案例6:输入数组[3, 6, 9, 12],gcd是3,输出3。 这样代码应该可以正确处理这些情况。 现在需要验证代码的正确性。例如,当数组是[5,10],操作后可以得到0和5,和为5,等于gcd。代码输出5,正确。 当数组是[3,5],gcd是1,操作后的最小和是1。例如,5-3=2,得到[3,2];然后3-2=1,得到[2,1];然后2-1=1,得到[1,1];然后1-1=0,得到[0,1],和为1,正确。 所以代码逻辑正确。 </think>要解决这个问,关键在于理解操作的数学性质:任意次操作后数组的最小和等于所有元素的最大公约数(GCD)。由于需要自己实现GCD计算,我们使用欧几里得算法来实现。 ### 方法思路 1. **操作性质**:每次用大的数减去小的数替换大的数,等价于欧几里得算法中的取模操作,因此数组元素的GCD保持不变。 2. **最小和推导**:最终数组的最小和等于所有元素的GCD(若所有元素为0则和为0)。 3. **自实现GCD**:使用欧几里得算法计算两个数的GCD,并处理负数情况(取绝对值后计算)。 ### C++实现代码 ```cpp #include <iostream> #include <vector> #include <cmath> // 用于abs函数 using namespace std; // 自实现的GCD计算函数(处理负数,取绝对值后计算) int my_gcd(int a, int b) { a = abs(a); // 取绝对值,确保处理负数情况 b = abs(b); while (b != 0) { // 欧几里得算法核心 int temp = a % b; a = b; b = temp; } return a; // 当b为0时,a即为GCD } int main() { int n; cin >> n; vector<int> nums(n); for (int i = 0; i < n; ++i) { cin >> nums[i]; } if (n == 0) { // 处理空数组(目可能不要求,但代码鲁棒性考虑) cout << 0 << endl; return 0; } // 计算所有元素的GCD int current_gcd = abs(nums[0]); // 初始GCD为第一个元素的绝对值 for (int i = 1; i < n; ++i) { current_gcd = my_gcd(current_gcd, nums[i]); if (current_gcd == 0) { // 提前终止:所有数已是0,GCD不可能更小 break; } } cout << current_gcd << endl; return 0; } ``` ### 代码解释 1. **自实现GCD函数**:`my_gcd`通过欧几里得算法计算两个数的GCD,先取绝对值处理负数,再通过循环取模直到余数为0。 2. **输入处理**:读取数组长度和元素值。 3. **特殊情况处理**:若数组为空,直接输出0(实际目可能保证数组非空)。 4. **计算全局GCD**:从第一个元素的绝对值开始,遍历数组逐步计算所有元素的GCD。 5. **输出结果**:最终的GCD即为操作后的最小数组和。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值