显然我们可以得出一个$O(n^2)$的dp方程
记$f(i)$为取到第$i$个音符时的最大分数,枚举下一个音符的位置$j$进行转移。
蓝后我们就可以用树状数组存下$f(i)$的最大值,每次用$logn$的复杂度每次询问$j=1 ightarrow i-t[i]$中最大$f(j)$值。
酱紫复杂度就变成了$O(nlogn)$
对于$f(i)$在位置$i+t[i]$之后才能作为转移的一个选择的问题,我们可以打一个延迟标记(ping函数),用类似链式前向星的结构存储(就像存边一样)。
每次到达一个$k$,就把每个从$k$开始起转移作用的$f(i)加入树状数组。
#include<cstdio> typedef long long ll; ll max(ll a,ll b){return a>b?a:b;} void read(ll &x){ char c=getchar();x=0; while(c<'0'||c>'9') c=getchar(); while('0'<=c&&c<='9') x=x*10+(c^48),c=getchar(); } #define N 1000005 ll s[N],a[N],t[N],f[N],ans; int n,cnt,hd[N],nxt[N],ed[N],poi[N]; void add(int x,ll v){for(;x<=n;x+=x&-x)s[x]=max(s[x],v);} ll ask(int x){ll re=0;for(;x;x-=x&-x)re=max(re,s[x]);return re;} void ping(int x,int y){ nxt[ed[x]]=++cnt; hd[x]=hd[x]?hd[x]:cnt; ed[x]=cnt; poi[cnt]=y; } int main(){ freopen("fc.in","r",stdin); freopen("fc.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;++i) read(t[i]); for(int i=1;i<=n;++i) read(a[i]); for(int i=1;i<=n;++i){ for(int j=hd[i];j;j=nxt[j]) add(poi[j],f[poi[j]]);//f[poi[j]]从i开始可以作为一个选择,加入树状数组 f[i]=a[i]*t[i]; if(i>t[i]) f[i]+=ask(i-t[i]);//查询1~i-t[i]的最优选择 if(i+t[i]<=n) ping(i+t[i],i);//把f(i)加入位置i+t[i]的标记中 ans=max(ans,f[i]); }printf("%lld",ans); return 0; }