P5502 [JSOI2015]最大公约数 题解

博客园同步

原题链接

简要题意:

  • 给定一个长度为 n n n 的序列 a a a,求出其中一个子串 S S S,使得 ∣ S ∣ × gcd ⁡ ( x ∈ S ) |S| \times \gcd(x \in S ) S×gcd(xS). 求这个最大值。
  • 给定一个长度为 n n n 的序列 a a a,求出一个区间 [ l , r ] [l,r] [l,r] 使得 ( r − l + 1 ) × gcd ⁡ i = l r a i (r-l+1) \times \gcd_{i=l}^r a_i (rl+1)×gcdi=lrai 最大.求这个最大值。

n ≤ 1 0 5 , 1 ≤ a i ≤ 1 0 12 n \leq 10^5 , 1 \leq a_i \leq 10^{12} n105,1ai1012.

两种不同表达的题意而已。

首先,抛出一个非常有用的结论:

  • 最大值对应的区间长度不超过 log ⁡ n \log n logn.

为什么呢?

固定右区间 r r r ,枚举 l l l 向左拓展,每拓展一次, gcd ⁡ \gcd gcd 要么不变,要么 ≤ \leq 原来 gcd ⁡ \gcd gcd 的一半。 这样,不同的 gcd ⁡ \gcd gcd 的值最多只有 log ⁡ n \log n logn 个.

这样我们给定 r r r 用类似单调队列的方法维护即可。

时间复杂度: O ( n log ⁡ n log ⁡ a i ) \mathcal{O}(n \log n \log a_i) O(nlognlogai).

实际得分: 100 p t s 100pts 100pts.

#pragma GCC optimize(2)
#include
using namespace std;

typedef long long ll;
const int N=1e6+1;
#define L (x<<1)
#define R (x<<1)+1

inline ll read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	ll x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}

inline void write(ll x) {
	if(x<0) {putchar('-');write(-x);return;}
	if(x<10) {putchar(char(x%10+'0'));return;}
	write(x/10);putchar(char(x%10+'0'));
}

int n;
queue<int> q,qq;
ll a[N],ans=0;

inline ll gcd(ll n,ll m) {return !m?n:gcd(m,n%m);} //计算 gcd
signed main() {
	n=read(); a[0]=-1;
	for(int i=1;i<=n;i++) {
		a[i]=read(); int l=0;
		while(!q.empty()) {
			int x=q.front(); q.pop(); //printf("%d\n",x);
			a[x]=gcd(a[x],a[i]);
			ans=max(ans,a[x]*(i-x+1)); //更新答案
			if(a[x] == a[l]) continue;
			qq.push(x); l=x; //新的决策点
		} ans=max(ans,a[i]);
		while(!qq.empty()) {
			q.push(qq.front()); qq.pop();
		} if(a[l] != a[i]) q.push(i);
	} write(ans);
	return 0;
}


你可能感兴趣的:(贪心,队列,数论)