P7009 [CERC2013] Magical GCD

题目链接

luogu P7009

题面

题面翻译

T T T 组询问,每次给出 n n n 个数 a i a_i ai
你需要找到这个数组的一个子序列(要求编号连续),使得该序列中所有数的最大公约数和序列长度的乘积最大,并输出这个最大值。

题目描述

The Magical GCD of a nonempty sequence of positive integers is defined as the product of its length and the greatest common divisor of all its elements.
Given a sequence ( a 1 , … , a n ) (a_1, \ldots , a_ n) (a1,,an), find the largest possible Magical GCD of its connected subsequences.

输入格式

The first line of input contains the number of test cases T T T. The descriptions of the test cases follow:
The description of each test case starts with a line containing a single integer n n n, 1 ≤ n ≤ 100   000 1 \leq n \leq 100\, 000 1n100000. The next line contains the sequence a 1 , a 2 , … , a n a_1, a_2 , \ldots , a _ n a1,a2,,an, 1 ≤ a i ≤ 1 0 12 1 \leq a_ i \leq 10^{12} 1ai1012.

输出格式

For each test case output one line containing a single integer: the largest Magical GCD of a connected subsequence of the input sequence.

样例 #1

样例输入 #1

1
5
30 60 20 20 20

样例输出 #1

80

提示

Time limit: 8000 ms, Memory limit: 1048576 kB.
Central Europe Regional Contest (CERC) 2013

题意

在长度为 n n n的区间内选一个连续的子序列,要求子序列内所有的值的 g c d gcd gcd乘以子序列长度最大,输出这个值

思路

O ( n 3 ) O(n^3) O(n3)暴力

很简单,第一重循环枚举左端点,第二重循环枚举右端点,第三重循环求枚举区间内所有值的 g c d gcd gcd,更新答案,做完(时间复杂度 O ( n 3 ) O(n^3) O(n3)

O ( n 2 ) O(n^2) O(n2)暴力+树状数组

对于 O ( n 3 ) O(n^3) O(n3)暴力,我们可以对求子序列的 g c d gcd gcd进行树状数组预处理

由于求区间 g c d gcd gcd是一种可重复贡献问题,所以求值时可以将区间分成多份,所以我们可以使树状数组进行特定区间预处理

通过树状数组优化第三重循环后,时间复杂度变成了 O ( n 2 ) O(n^2) O(n2)

void build(){//树状数组预处理
	for(int j=1;j<=21;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]);
	return;
}
int query(int l,int r){//树状数组询问区间l~r的gcd
	int tmp=llog[r-l+1];
	return __gcd(st[l][tmp],st[r-(1<<tmp)+1][tmp]);
}

AC做法

因为 n n n达到 1 0 6 10^6 106的大小,导致 O ( n 2 ) O(n^2) O(n2)的做法会 T L E TLE TLE
这时我们来推一遍样例

5
30 60 20 20 20

先设当前区间的 g c d gcd gcd p p p

i = 1 i=1 i=1

j = 1 j=1 j=1时, p = 30 p=30 p=30
j = 2 j=2 j=2时, p = 30 p=30 p=30
j = 3 j=3 j=3时, p = 10 p=10 p=10
j = 4 j=4 j=4时, p = 10 p=10 p=10
j = 5 j=5 j=5时, p = 10 p=10 p=10

i = 2 i=2 i=2

j = 2 j=2 j=2时, p = 60 p=60 p=60
j = 3 j=3 j=3时, p = 20 p=20 p=20
j = 4 j=4 j=4时, p = 20 p=20 p=20
j = 5 j=5 j=5时, p = 20 p=20 p=20

i = 3 i=3 i=3

j = 3 j=3 j=3时, p = 20 p=20 p=20
j = 4 j=4 j=4时, p = 20 p=20 p=20
j = 5 j=5 j=5时, p = 20 p=20 p=20

i = 4 i=4 i=4

j = 4 j=4 j=4时, p = 20 p=20 p=20
j = 5 j=5 j=5时, p = 20 p=20 p=20

i = 5 i=5 i=5

j = 5 j=5 j=5时, p = 20 p=20 p=20

可以发现,随着 j j j的右移,所对应的 p p p也呈不上升的有序分布
什么是分布,观察当 i = 1 i=1 i=1 p p p
显而易见, p p p呈块状分布且具有递减性
而且如果要想让当前区块 g c d gcd gcd乘以区块长度最大,那就必须让右端点指向当前区块的末端点,所以我们将问题转化为了求区块的末端点


思考一下,有序的数列快速搜索一个元素
这不就是二分查找吗?!
对于每一个 i i i,我们不停地二分查找 j ∼ n j\sim n jn的区块的末端点,每次查找完就将 j j j指向当前区块末端点加一的下标,继续查找,直到 j j j超出 n n n
细节见代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int llog[100010]={-1},st[100010][22],j,l,r,k,mid,tmp,ans,t,n;
void build(){//树状数组预处理
	for(int j=1;j<=21;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]);
	return;
}
int query(int l,int r){//树状数组询问区间l~r的gcd
	tmp=llog[r-l+1];
	return __gcd(st[l][tmp],st[r-(1<<tmp)+1][tmp]);
}
signed main(){
    scanf("%lld",&t);
    for(int i=1;i<=100000;i++)llog[i]=llog[i>>1]+1;//log预处理 
	while(t--){
		scanf("%lld",&n);
		for(int i=1;i<=n;i++)scanf("%lld",&st[i][0]);
		build();//预处理树状数组 
		ans=-0x7fffffff;
		for(int i=1;i<=n;i++){
			j=i;//j从i开始 
			while(j<=n){
				l=i,r=n+1;//初始化l,r 
				k=query(i,j);//上一个区块的gcd,作为二分查找压缩的参考 
				while(l+1<r){
					mid=l+r>>1;
					if(query(i,mid)<k)r=mid;//如果i~mid的区间gcd小于参考值,则认为mid下标大于下一个区块末端的下标,向左继续二分 
					else l=mid;
				}
				ans=max(ans,k*(l-i+1));//更新答案 
				j=r+1;
			}
		}
		printf("%lld\n",ans);
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值