Description
给定一个长度为 N 的正整数序列Ai对于其任意一个连续的子序列
{Al,Al+1...Ar},我们定义其权值W(L,R )为其长度与序列中所有元素的最大公约数的乘积,即W(L,R) = (R-L+1) ∗ gcd (Al..Ar)。
JYY 希望找出权值最大的子序列。
Input
输入一行包含一个正整数 N。
接下来一行,包含 N个正整数,表示序列Ai
1 < = Ai < = 10^12, 1 < = N < = 100,000
Output
输出文件包含一行一个正整数,表示权值最大的子序列的权值。
考虑暴力:
枚举右端点,再暴力枚举左端点,更新答案,时间复杂度为 $O(n^2log(10^{12}))$.
当一段区间的 $gcd$ 的值都相同时,我们只会取左端点最靠左的,这样显然最优.
而右端点固定时,区间左端点越靠左, $gcd$ 的值会越小,那么我们就能减少决策点
由于已知能对答案起贡献的 $gcd$ 都是会不相同的,而每次 $gcd$ 向左都会减小,且至少 $/2$ (或不变)
那么每次只需枚举那 $O(log(10^{12}))$ 个左端点.
我们得到了一个做法:
依次枚举右端点,右端点每次增加 $1$,原先右端点所对应的决策点都要保留,并与新的右端点的值取 $gcd$,并加入右端点的值.
每次这么更新一下并删掉相同的数即可.
枚举右端点,再暴力枚举左端点,更新答案,时间复杂度为 $O(n^2log(10^{12}))$.
当一段区间的 $gcd$ 的值都相同时,我们只会取左端点最靠左的,这样显然最优.
而右端点固定时,区间左端点越靠左, $gcd$ 的值会越小,那么我们就能减少决策点
由于已知能对答案起贡献的 $gcd$ 都是会不相同的,而每次 $gcd$ 向左都会减小,且至少 $/2$ (或不变)
那么每次只需枚举那 $O(log(10^{12}))$ 个左端点.
我们得到了一个做法:
依次枚举右端点,右端点每次增加 $1$,原先右端点所对应的决策点都要保留,并与新的右端点的值取 $gcd$,并加入右端点的值.
每次这么更新一下并删掉相同的数即可.
#include <bits/stdc++.h>
#define setIO(s) freopen(s".in","r",stdin)
#define maxn 100004
#define ll long long
using namespace std;
ll gcd(ll a,ll b) {
return b?gcd(b,a%b):a;
}
ll arr[maxn],gc[maxn];
int pos[maxn],b[maxn];
int main() {
// setIO("input");
int n,i,j,top=0,tmp=0;
ll ans=0;
scanf("%d",&n);
for(i=1;i<=n;++i) {
scanf("%lld",&arr[i]);
for(j=1;j<=top;++j) gc[j]=gcd(gc[j],arr[i]);
gc[++top]=arr[i], pos[top]=i;
b[tmp=1]=1;
for(j=2;j<=top;++j) if(gc[j]!=gc[j-1]) b[++tmp]=j;
top=0;
for(j=1;j<=tmp;++j) {
gc[++top]=gc[b[j]],pos[top]=pos[b[j]];
ans=max(ans,1ll*(i-pos[top]+1)*gc[top]);
}
}
printf("%lld\n",ans);
return 0;
}