fjwc2019 D1T1 全连(dp+树状数组)

#178. 「2019冬令营提高组」全连

显然我们可以得出一个$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;
}
原文地址:https://www.cnblogs.com/kafuuchino/p/10426896.html