背景:
刷的水题越来越多了。
题目传送门:
https://www.luogu.org/problem/P5502
题意:
定义区间
[
l
,
r
]
[l,r]
[l,r]的贡献为
(
r
−
l
+
1
)
⋅
gcd
(
a
l
,
a
l
+
1
,
.
.
.
,
a
r
)
(r-l+1)\cdot \gcd(a_l,a_{l+1},...,a_{r})
(r−l+1)⋅gcd(al,al+1,...,ar)。求最大贡献。
思路:
容易想到这些数的
gcd
\gcd
gcd个数不会很多,据说是
Θ
(
log
值
域
)
\Theta(\log值域)
Θ(log值域)级别的。
考虑确定了右端点
x
x
x,我们之前已经得到了
x
−
1
x-1
x−1个
gcd
\gcd
gcd后缀,那么我们就可以将
x
x
x位置的贡献与这
x
−
1
x-1
x−1个贡献计算一下,或者在
x
x
x位置作为起点,新开一个后缀,起点从
x
x
x开始。
然后考虑
gcd
\gcd
gcd相同的数,我们只要起点尽可能靠前的哪一个即可,其余都是废的,排个序即可。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#define LL long long
#define INF (LL)1e9
using namespace std;
map<LL,bool> MAP;
int n;
LL ans=0;
LL a[100010];
struct node{LL x;int op;} b[100010];
LL gcd(LL x,LL y)
{
return !y?x:gcd(y,x%y);
}
bool cmp(node x,node y)
{
return x.op<y.op;
}
int main()
{
scanf("%d",&n);
ans=n;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
ans=max(ans,a[i]);
}
int t=0;
for(int i=1;i<=n;i++)
{
b[++t]=(node){a[i],i};
for(int j=1;j<=t;j++)
{
b[j].x=gcd(b[j].x,a[i]);
ans=max(ans,b[j].x*(i-b[j].op+1));
}
MAP.clear();
for(int j=1;j<=t;j++)
if(b[j].x==1||MAP[b[j].x]) b[j]=(node){0,INF}; else MAP[b[j].x]=true;
sort(b+1,b+t+1,cmp);
while(t&&b[t].op==INF) t--;
}
printf("%lld",ans);
}