一种把状态减少,只保留有效状态的DP
但是一个地方不太明白
首先,只和当前mod ai的值有关系,朴素的设法是:dp[i][j],%ai=j的最总和是多少。
然而实在不方便转移
而注意到,xi一定是单调不升的,所以i位置是xi,那么ans可以表示为xi*i+b的形式。只用保留b即可
神仙的状态设计:dp[i][j]表示,第i位的x为j时,所谓的b最大是dp[i][j]
显然也具有最优子结构的性质
考虑转移,i->i+1:思想:值域过大,尽量减少不必要的转移以减少状态数
枚举j,用dp[i][j]更新
首先发现,当j<a[i+1]时候,dp[i+1][j]=dp[i][j]
否则,考虑用dp[i][j]来给[0,j]一起做转移,把这些dp[i][k]的dp都看做dp[i][j],(正确性存疑,感觉可能dp[i][k]更小但是转移大了?)
从k=j,k越来越小的时候,取模后也会越来越小,这些转移过去都没有意义。(应该不能说是没意义,而是dp[]其实是同一个值!)
但是会mod到下一个循环节。也即k%a(i+1)=a(i+1)-1
对于y,<=j的最后一个%a(i+1)=a(i+1)-1的位置,这个可以是有意义的(dp[]不同了)
那么,对于比y还小的k,--k,取模之后也会越来越小,其实也没意义了。(dp[i][k]都和dp[i][y]相同)
每个数只会mod logn次
总复杂度也是O(nlognlogx)
懵了懵了不管了。
#include<bits/stdc++.h> #define reg register int #define il inline #define fi first #define se second #define mk(a,b) make_pair(a,b) #define numb (ch^'0') #define pb push_back #define solid const auto & #define enter cout<<endl #define pii pair<int,int> using namespace std; typedef long long ll; template<class T>il void rd(T &x){ char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);} template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');} template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');} template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');} namespace Modulo{ const int mod=998244353; int ad(int x,int y){return (x+y)>=mod?x+y-mod:x+y;} void inc(int &x,int y){x=ad(x,y);} int mul(int x,int y){return (ll)x*y%mod;} void inc2(int &x,int y){x=mul(x,y);} int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;} } //using namespace Modulo; namespace Miracle{ const int N=200000+5; int n; ll a[N]; map<ll,ll,greater<ll> >mp; ll tmp[5*N]; int main(){ rd(n); for(reg i=1;i<=n;++i) rd(a[i]); mp[a[1]-1]=0; for(reg i=1;i<n;++i){ int ptr=0; for(solid x:mp){ if(x.fi<a[i+1]) break; mp[a[i+1]-1]=max(mp[a[i+1]-1],mp[x.fi]+(ll)i*a[i+1]*((x.fi-a[i+1]+1)/a[i+1])); mp[x.fi%a[i+1]]=max(mp[x.fi%a[i+1]],mp[x.fi]+(ll)i*(x.fi-x.fi%a[i+1])); tmp[++ptr]=x.fi; } while(ptr) mp.erase(tmp[ptr--]); } ll ans=0; for(solid x:mp){ ans=max(ans,(ll)x.fi*n+x.se); } ot(ans); return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* */
同样的状态减少转移,有点类似整体DP
把[0,j]的dp都看成dp[i][j]取max就覆盖了