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
各部分在文中已经拆分提供代码。