题目传送门:http://codeforces.com/contest/889/problem/E
题目大意:有一个长度为n的序列a。定义函数 f(x,n) 值为 xmodan 。并定义函数 f(x,i)(1<=i<n) 值为 xmodai+f(xmodai,i+1) 。求最大的 f(x,1) 。 n<=2∗105,1<=ai<=1013 。
题目分析:一开始想错了,以为是道水题。容易发现最优解必定存在一个i,使得最初的x不断取模下去,到第i位恰好为 ai−1 。于是维护一个单调栈,枚举这个 ai−1 。往右走的时候二分到第一个小于当前值的地方;往左走的时候贪心地变大,二分第一个大于当前值两倍的地方。由于往左走至少乘以2,往右走至少除以2,时间复杂度为 O(nlog(n)log(1013)) 。
然而往左走贪心地变大是错的,举个反例:
a={13,19,8,2,3,8,10,7,12,6}
当i=4的时候,贪心地往左走,前四个值为{7,7,7,1},然而最优解应该是{11,11,3,1}。
正解的做法是一个很神奇的DP(以下为题解翻译)。设 x=初始值moda1moda2……modai ,则f[i][j]=k表示做到第i位,当x取0~j时1~i的总贡献为xi+k,最大化k。设M=a[i+1],则若 j<M ,f[i][j]=k变为f[i+1][j]=k;若j>=M,则f[i][j]=k分为最优的两部分: f[i+1][jmodM]=k+i∗(j−jmodM) , f[i+1][M−1]=k+i∗((j+1)/M∗M−M) 。由于M-1是定值,所以保留f[i+1][M-1]的最大值即可,这样就保证做到第i位时,最多有i个状态。又因为j模一个比它小的数,值至多变为原先的一半,所以用map存储这些状态,每一次将大于等于M的j拉出来更新,时间复杂度就是 O(nlog(n)log(1013)) 。
不会用STL的map?还不快像我一样手写Treap!
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=200100;
const int Lg=40;
const long long M1=998244353;
const long long M2=1000000007;
const long long M3=1333333331;
typedef long long LL;
LL seed;
struct Tnode
{
LL val,cnt;
int fix;
Tnode *lson,*rson;
} tree[maxn*Lg];
Tnode *Root=NULL;
int cur=-1;
LL a[maxn];
int n;
LL ans=0;
int Rand()
{
seed=(seed*M1+M2)%M3;
return (int)seed;
}
Tnode *New_node(LL Val,LL Cnt)
{
cur++;
tree[cur].val=Val;
tree[cur].cnt=Cnt;
tree[cur].fix=Rand();
tree[cur].lson=tree[cur].rson=NULL;
return tree+cur;
}
void Right_turn(Tnode *&P)
{
Tnode *W=P->lson;
P->lson=W->rson;
W->rson=P;
P=W;
}
void Left_turn(Tnode *&P)
{
Tnode *W=P->rson;
P->rson=W->lson;
W->lson=P;
P=W;
}
void Insert(Tnode *&P,LL Val,LL Cnt)
{
if (!P) P=New_node(Val,Cnt);
else
if ( Val<P->val || ( Val==P->val && Cnt<P->cnt ) )
{
Insert(P->lson,Val,Cnt);
if ( P->lson->fix < P->fix ) Right_turn(P);
}
else
{
Insert(P->rson,Val,Cnt);
if ( P->rson->fix < P->fix ) Left_turn(P);
}
}
Tnode *Last(Tnode *P)
{
if (!P->rson) return P;
return Last(P->rson);
}
void Delete(Tnode *&P,LL Val,LL Cnt)
{
if ( Val==P->val && Cnt==P->cnt )
if (P->lson)
if (P->rson)
if ( P->lson->fix < P->rson->fix )
{
Right_turn(P);
Delete(P->rson,Val,Cnt);
}
else
{
Left_turn(P);
Delete(P->lson,Val,Cnt);
}
else P=P->lson;
else P=P->rson;
else
if ( Val<P->val || ( Val==P->val && Cnt<P->cnt ) ) Delete(P->lson,Val,Cnt);
else Delete(P->rson,Val,Cnt);
}
void Dfs(Tnode *P)
{
if (!P) return;
Dfs(P->lson);
Dfs(P->rson);
ans=max(ans,(long long)n*P->val+P->cnt);
}
int main()
{
freopen("E.in","r",stdin);
freopen("E.out","w",stdout);
scanf("%d",&n);
seed=n;
for (int i=1; i<=n; i++) scanf("%I64d",&a[i]);
Insert(Root,a[1]-1LL,0LL);
for (int i=2; i<=n; i++)
{
LL Max=-1;
Tnode *P=Last(Root);
while ( P->val>=a[i] )
{
LL Val=P->val,Cnt=P->cnt;
Delete(Root,Val,Cnt);
LL temp=(Val+1LL)/a[i]-1LL;
Max=max(Max,Cnt+temp*(long long)(i-1)*a[i]);
if (Val%a[i]!=a[i]-1LL) temp++;
Insert(Root,Val%a[i],Cnt+temp*(long long)(i-1)*a[i]);
P=Last(Root);
}
if (Max>=0) Insert(Root,a[i]-1LL,Max);
}
Dfs(Root);
printf("%I64d\n",ans);
//printf("%d\n",sizeof(tree)/1024/1024);
return 0;
}