CF1535B Array Reodering 题解

题目大意

首先,本题含有多组数据

对于每组数据,考虑两个下标 \(i,j\) 在一个长度为 \(n\) 的序列 \(a\) 中,若 \(1\leqslant i < j \leqslant n \land gcd(a_i,2a_j)>1\) 则称这对 \((i,j)\)优美的数对,请问怎样安排这个序列使得有最多的优美的数对,输出这个最大值。

思路分析

很显然,\(2a_j\) 是本题的突破口。

根据简单的数学我们知道,如果 \(a_j\) 为奇数,\(2a_j\) 就是偶数,同时又有 \(a_i\) 为偶数,那偶数和偶数之间的 \(gcd\) 肯定是大于 \(1\) 的。

为了既可以把 \(a_j\) 变成偶数,又能使前面的 \(a_i\) 为偶数,可以考虑把偶数放在奇数前面,让后面的奇数占掉下标 \(j\) ,偶数占掉下标 \(i\) ,不但使得 \(i<j\) ,同时也造出了一大堆合法的数对。

代码解析

第一步:把偶数放前面,奇数放后面

在读入的时候,用两个数组,一个数组存偶数,一个数组存奇数

for(int i=0;i<n;i++){
	cin>>a[i];
	if(a[i]%2==1){
		b[tot]=a[i];//tot是指针,指向奇数数组的下一位。
		tot++;//指针后移
	}
	else{
		c[tnt]=a[i];//tnt也是指针,指向偶数数组的下一位。
  		tnt++;//指针后移
	}
}

所以现在我们就把偶数和奇数分离了出来。

指针的另外一个作用其实是记录奇数和偶数各有多少。因此现在我们可以把前 \(tnt\)\(a_i\) 变成 \(c_i\),而剩下的数都变成 \(b_{i-tnt}\)

//我用的两个for循环可以变成一个,但是这样更加直观。
for(int i=0;i<tnt;i++){
		a[i]=c[i];//把a序列中的前面tnt个数变成偶数。
}
for(int i=tnt;i<n;i++){
	a[i]=b[i-tnt];//把a序列中剩下的数变成奇数。
}

注意:在将奇数赋值回序列 \(a\) 中时,\(b\) 数组的下标要减去一个 \(tnt\)

第二步:做暴力

首先先写一个 \(gcd\) 函数。

int gcd(int x,int y){
   if(y==0){
   	return x;
   }
   else{
   	return gcd(y,x%y);//辗转相除法求gcd
   }
}
//这是速度较快而且写法简单的gcd方法。
//甚至还可以用三目运算符压行成一行。

然后做一个大暴力就可以了。

for(int i=0;i<n;i++){
	for(int j=i+1;j<n;j++){ //因为i<j,所以可以直接从i+1开始暴力。
		if(gcd(a[i],2*a[j])>1){//我们的快速gcd
			cnt++;//记录个数
		}
	}
}

注意事项

  • \(tnt,\ tot,\ cnt\) 每组数据结束后一定要清零
  • 要搞多组数据

AC code

各部分在文中已经拆分提供代码。

AC记录

posted @ 2022-01-26 17:41  Shunpower  阅读(33)  评论(0编辑  收藏  举报