luogu P5502 [JSOI2015]最大公约数|st表+二分

题目描述

给定一个长度为 \(N\) 的正整数序列 \(A_i\)​ 。

对于其任意一个连续的子序列 \(A_l,A_{l+1},...,A_r\)​ ,我们定义其权值 \(W(L,R)\) 为其长度与序列中所有元素的最大公约数的乘积,即 \(W(L,R) = (R-L+1) × \gcd (A_l,...,A_r)\)

JYY 希望找出权值最大的子序列。

输入格式

输入一行包含一个正整数 \(N\)

接下来一行,包含 \(N\) 个正整数,表示序列 \(A_i\)​ 。

输出格式

输出文件包含一行一个正整数,表示权值最大的子序列的权值。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
#define int long long
const int N=1e5+5;
int n,a[N],st[N][21],logN[N];
int gcd(int a,int b){
	if(b==0)return a;
	return gcd(b,a%b);
}
inline int getgcd(int l,int r){
	int s=logN[r-l+1];
	return gcd(st[l][s],st[r-(1<<s)+1][s]);
}
signed main(){
	cin>>n;
	int ans=0;
	logN[0]=-1;
	for(int i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		st[i][0]=a[i];
		logN[i]=logN[i>>1]+1;
		ans=max(ans,a[i]);
	}
	for(int j=1;j<=logN[n];j++)
	for(int i=1;i+(1<<j)-1<=n;i++)
		st[i][j]=gcd(st[i][j-1],st[i+(1<<(j-1))][j-1]);
	
	for(int i=1,l,r,mid,lst,res;i<=n;i++){
		res=a[i],lst=i;
		while(1){
			l=lst,r=n;
			while(l<=r){
				mid=(l+r)>>1;
				if(res==getgcd(i,mid))l=mid+1;
				else r=mid-1;
			}
			ans=max(ans,(l-i)*res);
			if(l>n)break;
			res=getgcd(i,l);
			lst=l;
			
		}
	}
	cout<<ans<<endl;
}
posted @ 2022-02-24 13:30  白木偶君  阅读(26)  评论(0编辑  收藏  举报