P5502 [JSOI2015]最大公约数

不具有可减性的区间问题可以试着考虑分治。

设当前区间是 \([l,r]\),令 \(mid=\lfloor\frac{l+r}{2}\rfloor\),递归 \([l,mid-1]\)\([mid+1,r]\) 求解,然后考虑经过 \(mid\) 的答案,显然不会少算

考虑枚举 \([l,mid]\) 作为左端点的答案和 \([mid,r]\) 作为右端点的答案,当另一端可以无代价拓展时就进行拓展。

这样可能会漏掉一些情况,假设 \([i,j]\) 满足 \(\gcd(a_i,a_{i+1},\cdots,a_{mid})=p\)\(\gcd(a_{mid},\cdots,a_{j-1},a_j)=q\)

\(\gcd(p,q)>1\)一定会漏掉。不过此时我们发现 \(2\times\gcd(p,q)\leq\min\{p,q\}\),也就是说只要计算了 \([i,mid]\)\([mid,j]\) 的答案就无须再算 \([i,j]\) 的答案,因为较长的那个区间的答案不会更劣。

而这两者肯定都是算过的(可能无代价拓展过),所以正确性没有问题

时间复杂度 \(O(N\log N\log A)\)

code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define Max(x,y)((x)>(y)?x:y)
#define For(i,x,y)for(i=x;i<=(y);i++)
ll a[100005];
ll gcd(ll _,ll __)
{
	return(!_?__:gcd(__%_,_));
}
ll solve(int l,int r)
{
	if(l>r)return 0;
	ll w,ret,tmp;
	int mid=(l+r)>>1,i,j;
	i=j=mid;
	ret=w=a[mid];
	while(l<=i)
	{
		while(j<r&&!(a[j+1]%w))j++;
		ret=Max(ret,w*(j-i+1));
		w=gcd(w,a[--i]);
	}
	i=j=mid;
	w=a[mid];
	while(j<=r)
	{
		while(i>l&&!(a[i-1]%w))i--;
		ret=Max(ret,w*(j-i+1));
		w=gcd(w,a[++j]);
	}
	tmp=solve(l,mid-1);
	ret=Max(ret,tmp);
	tmp=solve(mid+1,r);
	return Max(tmp,ret);
}
int main()
{
	int n,i;
	scanf("%d",&n);
	For(i,1,n)scanf("%lld",&a[i]);
	printf("%lld",solve(1,n));
	return 0;
}
posted @ 2021-06-15 23:57  Biadocy  阅读(52)  评论(0编辑  收藏  举报