样例输入:
2
2
2 2
4
5 2 3 5
样例输出:
2
5
一丶引入GCD
GDC,即最大公约数。便捷求法如下。
int GCD(int a,int b){
return !a?b:GCD( b%a , a );
}
二丶对题面的解析。
我们需要将一个数列分割得到一个最大公约数较大的数列,显然存在N多种割法。同样显然不割的时候GCD最大,也就是本身,但我们需要至少割为两段。
假设我们将一个字符串已经割了N次,现在将他割为N+1次,我们可以论证GCD不会变大,即不变或者变小。将割了N次的N段数列割为N+1段,显然需要从前面的N段中拿出部分数字来组合成新的一段数列,从数列中拿出数字且最后得到的数段依旧连续,我们有两种方式拿。
一,从一个连续数列里面拿,若变成了最大公约数更大的几个数列,那么他们合起来也会满足这个更大的最大公约数,故GCD不会变大。
二,从相邻的两个数列中拿,如果 A,B两个数列分割变成了acb三个数列,且前者的GCD小于后者的GCD,那么我们重新将cb合起来同样满足后来更大的GCD,只能说明AB的割法不优,将AB的割法换成更优的,我们同样可以得出结论,GCD不会随着割数变大而变大。
故这道题便有了解,即分割成2个数列即可,我们用前缀和和后缀和便可以得到这个数列的分割的所有情况,数据量为1e5且a的数据量为1e9,数列和最大为 1e14,我们需要用longlong储存。
代码实现如下
#include<bits/stdc++.h>
using namespace std;
long long int s[100005];//前缀和
long long int e[100005];//后缀和
long long int num[100005];//记录数组
long long GCD( long long a , long long b ){
return !a?b:GCD( b%a , a );
}
int main(){
int n;cin>>n;
while( n-- ){
int length;
cin>>length;
int mem = 0;
for ( int k = 0 ; k < length ; ++k ){
cin>>num[k];
mem+=num[k];
s[k]=mem;
}
mem = 0;
for ( int k = length - 1 ; k >= 0 ; --k ){
mem+=num[k];
e[k] = mem;
}
int min = -1;int gcd = 0;
for ( int k = 0 ; k < length - 1 ; ++k ){
gcd = GCD( s[k] , e[k] - num[k] );
if ( gcd > min )
min = gcd;
}
cout<<min<<endl;
}
}