给定一个长度为 的正整数序列 ,令 。
求 为非负整数时, 的最大值。
,。
记 , 也就是前缀和。
设状态 表示考虑了 , 且 ,那么显然这个状态下最大的 为 。
这是图解,最大的 即为 前面那个楼梯的面积:

考虑转移,有两种情况:
-
:无事发生,转移到 ;
-
:需要取模,分成两种子情况:
- :蓝色部分,转移到 ;
- :红色部分,转移到 ;
图解(绿色是第一种转移 增加的部分,黄色是第二种):
不难发现,第二种转移的 是固定的,那么只需要将 取 即可,所以每个 最多只会贡献一种新状态,并且由于最多取模 次( 是值域),所以状态数是 的,用 set
优化转移可以做到 的时间复杂度。
代码如下:
#include <iostream>
#include <cstdio>
#include <set>
using namespace std;
const int S=200005;
inline void fio(string name)
{
freopen((name+".in").c_str(),"r",stdin);
freopen((name+".out").c_str(),"w",stdout);
}
struct node
{
long long r,s;
inline bool operator<(node b) const
{
return r==b.r?s>b.s:r>b.r;
}
};
int n,m;
long long a[S];
set<node> dp;
int main()
{
fio("mod");
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
dp.insert((node){a[1]-1,0});
for(int i=2;i<=n;i++)
{
long long mx=-114;
for(node u:dp)
{
if(u.r<a[i]) break;
dp.insert((node){u.r%a[i],u.s+(i-1)*(u.r-u.r%a[i])});
mx=max(mx,u.s+(i-1)*(u.r-u.r%a[i]-a[i]));
}
while((*dp.begin()).r>=a[i]) dp.erase(dp.begin());
if(mx!=-114) dp.insert((node){a[i]-1,mx});
}
long long ans=0;
for(node u:dp) ans=max(ans,n*u.r+u.s);
printf("%lld\n",ans);
return 0;
}