1. 程式人生 > >JZOJ-senior-3780. 【NOIP2014模擬8.17】Magical GCD

JZOJ-senior-3780. 【NOIP2014模擬8.17】Magical GCD

Time Limits: 2000 ms Memory Limits: 131072 KB

Description

對於一個由正整陣列成的序列, Magical GCD 是指一個區間的長度乘以該區間內所有數字的最大公約數。給你一個序列,求出這個序列最大的 Magical GCD。

Input

單個測試點包含多組資料。 輸入的第一行是一個整數T表示資料組數。 每組資料的第一行是一個整數N,描述序列長度。 接下來N個數字,描述這個序列元素A[i]。

Output

對於每組測試資料輸出一行,包含一個整數,表示序列最大的 Magical GCD。

Sample Input

1 5 30 60 20 20 20

Sample Output

80

Data Constraint

對於 30%分值的資料,N <= 10,T <= 11,000, A[i] <= 100 對於剩餘 70%分值的資料,N <= 100,000,T <= 20, A[i] <= 10^12 C/C++選手讀入和輸出 64 位整數請使用%lld。

Hint

【樣例說明】 對於子區間 60 20 20 20,長度為 4,最大公約數為 20,此段 Magical GCD 為 80.

【評分標準】 對於每個測試點,你的輸出必須和標準輸出一致得到全部分數,否則不得分。

Solution

  • 不難發現,固定了左端點後,右端點往右移GCD是單調不增的
  • 考慮列舉右端點 iib[j]b[j] 表示區間 [j,i][j,i] 的GCD
  • 我們發現對於 b[j]==b[j1]b[j]==b[j-1] ,我們只需要保留 j1j-1 ,而不需要 jj ,為什麼呢?
  • 既然 b[j]==b[j1]b[j]==b[j-1] ,那麼當右端點繼續右移時,他們的到右端點的GCD是一樣的
  • j1j-1 的長度顯然更優,那麼 jj 就沒有存在的必要,我們用連結串列維護一下,跳過不必要的區間就好了

如果我的表述不清楚,可以看這個 在這裡插入圖片描述

Code

#include<algorithm>
#include<cstdio> using namespace std; const int N=1e5+5; int T,n,l[N],r[N]; long long ans,a[N],b[N]; long long gcd(long x,long y) { return y?gcd(y,x%y):x; } int main() { freopen("magical.in","r",stdin); freopen("magical.out","w",stdout); scanf("%d",&T); while(T--) { scanf("%d",&n),ans=0; for(int i=1;i<=n;++i) { scanf("%lld",&a[i]),b[i]=a[i]; l[i]=i-1,r[i]=i+1; } for(int i=1;i<=n;++i) for(int j=1;j<=i;j=r[j]) { b[j]=gcd(b[j],a[i]); ans=max(ans,b[j]*(i-j+1)); if(b[j]==b[j-1]) r[l[j]]=r[j],l[r[j]]=l[j]; } printf("%lld\n",ans); } }