正题
题目链接:https://www.luogu.com.cn/problem/P5502
题目大意
n n n个数,求一个 L , R L,R L,R使得最大化 ( R − L + 1 ) ∗ g c d ( a L , a L + 1 , a L + 1 . . . a R − 1 , a R ) (R-L+1)*gcd(a_L,a_{L+1},a_{L+1}...a_{R-1},a_R) (R−L+1)∗gcd(aL,aL+1,aL+1...aR−1,aR)
解题思路
考虑分治,答案分为两种可能
- 在 l ∼ m i d l\sim mid l∼mid或 m i d + 1 ∼ r mid+1\sim r mid+1∼r中,分治下去求即可
- L L L在 l ∼ m i d l\sim mid l∼mid中, R R R在 m i d + 1 ∼ r mid+1\sim r mid+1∼r中,此时考虑如何计算这个东西
显然 a m i d a_{mid} amid是一定要在答案中的,而如果目前的 g c d gcd gcd为 w w w,且左右端点扩展可以使得 w w w不变时那么就一定是扩展的最优,所以我们每次都扩展到无法再扩展时更新答案。无法扩展时我们有两种扩展方法,一种是左端点向左,一种是右端点向右,我们分两次一次往左一次往右即可。
时间复杂度 O ( n log n log a i ) O(n\log n\log a_i) O(nlognlogai)
c o d e code code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e5+10;
ll n,a[N],ans;
void solve(ll l,ll r){
if(l==r)return;
ll mid=(l+r)>>1;
solve(l,mid);solve(mid+1,r);
ll L=mid,R=mid,w=a[mid];
while(l<=L&&R<=r){
while(l<L&&__gcd(w,a[L-1])==w)L--;
while(R<r&&__gcd(w,a[R+1])==w)R++;
ans=max(ans,(R-L+1)*w);
if(l<L)w=__gcd(w,a[--L]);
else break;
}
L=mid,R=mid,w=a[mid];
while(l<=L&&R<=r){
while(l<L&&__gcd(w,a[L-1])==w)L--;
while(R<r&&__gcd(w,a[R+1])==w)R++;
ans=max(ans,(R-L+1)*w);
if(R<r)w=__gcd(w,a[++R]);
else break;
}
return;
}
int main()
{
scanf("%lld",&n);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
solve(1,n);
printf("%lld",ans);
}