Codeforces 1667B Optimal Partition 数据结构维护dp

文章目录


很有味道的一场比赛,BC两个题的想法都非常精彩。
题目链接

题意

定义一个区间的贡献 f ( x ) f(x) f(x)为该区间的长度乘上该区间所有数之和的符号。问将一个序列分成若干区间的贡献之和的最大值。

题解

首先可以写出显然的 n 2 n^2 n2动态规划做法。
d p i = m a x j = 0 i − 1 d p j + f ( j + 1 , i ) dp_i=max_{j=0}^{i-1}dp_j+f(j+1,i) dpi=maxj=0i1dpj+f(j+1,i)
分析区间贡献的性质,发现最优解中若 f ( x ) f(x) f(x)的贡献为负,则 x x x的长度最多为1。可以用维护前缀最大值的树状数组或者线段树来完成这个 d p dp dp的优化。
d p i dp_i dpi的初始值设为 d p i − 1 + f ( i , i ) dp_{i-1}+f(i,i) dpi1+f(i,i),然后用特判处理 [ 1 , i ] [1,i] [1,i]区间之和为正的情况,最后求 d p j = 1 → i dp_{j=1\to i} dpj=1i中的最大值,维护的时候用 d p x − x dp_x-x dpxx来更新即可。
注意序列的前缀和要按照大小排序更新,这样就可以通过了,应该没有什么其他问题,谢谢大家。

const int yuzu=5e5;
typedef ll fuko[yuzu|10];
fuko a,c,o,d,zw,dp;
int main() {
  for (int t=read();t--;) {
    int n=read(),i;
    using pli=pair<ll,int>;
    vector<pli> v; v.push_back({-yuzu,-yuzu});
    *o=*dp=*d=0;
    for (i=1;i<=n;++i) o[i]=read()+o[i-1],v.push_back(pli(o[i],-i));
    sort(v.begin()+1,v.end());
    auto add=[&](int x,ll v) {
      for (;x<=n;x+=x&-x) zw[x]=max(zw[x],v);
    };
    auto query=[&](int x) {
      ll ans=-1e6;
      for (;x;x&=x-1) ans=max(ans,zw[x]);
      return ans;
    };
    for (i=1;i<=n;++i) d[-v[i].second]=i,zw[i]=-1e6;
    for (i=1;i<=n;++i) {
      dp[i]=max(dp[i-1]+(o[i]-o[i-1]>0?1:o[i]-o[i-1]<0?-1:0),query(d[i])+i);
      if (o[i]>0) dp[i]=i;
      add(d[i],dp[i]-i);
    }
    printf("%lld\n",dp[n]);
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值