【洛谷】P7009 [CERC2013] Magical GCD

原题链接

题意

T 组询问,每次给出 n 个数 ai

你需要找到这个数组的一个子序列(要求编号连续),使得该序列中所有数的最大公约数和序列长度的乘积最大,并输出这个最大值。

数据范围

1n105,1ai1012

思路

参考了这篇博客

对于原序列的一个子区间,如果固左端点 l 不断往右延伸,设当前区间的最大公约数为 g,现在要把元素 x 扩展进来,新区间的最大公约数要么还是 g,要么就至少减少一半(根据 gcd 的性质),那么区间的 gcd 就至多更新 logal

因为题目所求的是最大值,那么对于 gcd 相同的区间,肯定会选择长度最长的区间。那么就可以从前往后枚举 ai,用一个队列 q 记录这些不同的左端点,同时用 ai 去更新这些左端点,并同时更新答案。

code:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1e5+10;
#define LL long long
queue<int>q,r;
int n;LL a[N],res;
LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);}
int main()
{
	int T;scanf("%d",&T);
	while(T--)
	{
		scanf("%d",&n);res=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);LL last=0;res=max(res,a[i]);
			while(!q.empty())
			{
				int x=q.front();q.pop();a[x]=gcd(a[x],a[i]);//x就是最小的满足区间[x,i]的gcd为a[x]的左端点 
				res=max(res,a[x]*(i-x+1));
				if(a[x]==a[last]) continue;//一些左端点就可以合并掉 
				r.push(x);last=x;
			}
			while(!r.empty())
			{
				q.push(r.front());
				r.pop();
			}
			if(a[last]!=a[i]) q.push(i);//i也可以作为一个新的左端点 
	 	}
		printf("%lld\n",res);
		while(!q.empty()) q.pop();
	}
	return 0;
}
posted @   曙诚  阅读(46)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示