#动态规划#CF889E Mod Mod Mod

题目传送门


分析

这道题有一个很妙的地方就是将一段前缀整体一起做。

\(dp[i][j]\) 表示\(x\) 被前 \(i\) 个数取模后答案最大,并且 \(j\) 为取得此答案的最大值

最后再对 \(dp[i][j]+n*j\) 取最大值即可。

如果 \(j\)\(a_i\) 取模之后仍然保留 \(j\),那么 \(dp[i][j\bmod a_i]=\max\{dp[i-1][j]+(j-j\bmod a_i)*(i-1)\}\)

如果不保留 \(j\),那么 \(dp[i][a_i-1]=\max\{dp[i-1][j]+\left\lfloor \frac{j-(a_i-1)}{a_i}\right\rfloor*a_i*(i-1)\}\)

然后取模之后会减半,所以时间复杂度为 \(O(n\log n\log a_i)\)


代码

#include <cstdio>
#include <cctype>
#include <map>
#include <algorithm>
using namespace std;
const int N=200011;
typedef long long lll;
map<lll,lll>dp; lll n,a[N],ans;
map<lll,lll>::iterator it;
lll iut(){
    lll ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
int main(){
    n=iut();
    for (int i=1;i<=n;++i) a[i]=iut();
    dp[a[1]-1]=0;
    for (int i=2;i<=n;++i){
        for (it=dp.lower_bound(a[i]);it!=dp.end();dp.erase(it++)){
            lll x=it->first,y=it->second,t0=dp[x%a[i]],t1=dp[a[i]-1];
            if (t0<y+(x-x%a[i])*(i-1)) dp[x%a[i]]=y+(x-x%a[i])*(i-1);
            if (t1<y+(x+1-a[i])/a[i]*a[i]*(i-1)) dp[a[i]-1]=y+(x+1-a[i])/a[i]*a[i]*(i-1);
        }
    }
    for (it=dp.begin();it!=dp.end();dp.erase(it++)){
        lll x=it->first,y=it->second;
        if (ans<x*n+y) ans=x*n+y;
    } 
    return !printf("%lld",ans);
}
posted @ 2022-03-22 20:12  lemondinosaur  阅读(45)  评论(0编辑  收藏  举报