Manthan, Codefest 19 E. Let Them Slide

题目链接: http://codeforces.com/contest/1208/problem/E

题意:

你现在有 n n n 行数组,他们被放在一个 m m m 列的箱子中,数组的长度如果不到 m m m 则可以左右移动,现在问你对于每一列 x x x ,经过数组移动后这一列可以达到的最大值为多少。

做法:

对于不出现负数的情况:

  • L L L 个位置,每个位置上只能取到区间 [ m a x ( 1 , i − ( w − L ) ) , i ] [max(1,i-(w-L)),i] [max(1,i(wL)),i] 中的最大值。
  • L L L 个位置,每个位置上只能取到区间 [ L − i + 1 , m i n ( L , n ) ] [L-i+1,min(L,n)] [Li+1,min(L,n)] 中的最大值。.
  • 中间的任意位置,均可以取到 [ 1 , L ] [1,L] [1,L] 中的最大值。

前两个情况,我们可以用单调栈来维护最大值进行更新。对于第三种情况,可以用差分来维护。

对于出现负数的情况:

因为我们可以将数组推至最左和最右,从而使答案加上 0 0 0 而不至于减少。

m − L m-L mL 个位置和后 m − L m-L mL 个位置都可以通过将数组移动至最左和最右端来使结果不减少。除了左边的 L L L 长度和右边的 L L L 长度外的中间值也可以不减少。

分类讨论一下即可。

代码

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;

const int maxn=1000005;
int Q[maxn],l,r,n,m,bac;
ll Base[maxn],Add[maxn],a[maxn],ma;
int main(){
    scanf("%d%d",&n,&m);
    rep(i,1,n){
        int L; scanf("%d",&L); ma=0;
        rep(j,1,L) scanf("%lld",&a[j]),ma=max(ma,a[j]);
        l=1,r=0; bac=max(L+1,m-L+1);
        Add[L+1]+=ma,Add[bac]-=ma;
        rep(j,1,L){
            while(l<=r&&j-Q[l]>m-L) l++;
            while(l<=r&&a[j]>=a[Q[r]]) r--;
            Q[++r]=j;
            if(a[Q[l]]>=0||j>m-L) Base[j]+=a[Q[l]];
        }
        l=1,r=0;
        for(int j=m;j>=bac;j--){
            int k=j-(m-L);
            while(l<=r&&Q[l]-k>m-L) l++;
            while(l<=r&&a[k]>=a[Q[r]]) r--;
            Q[++r]=k;
            if(a[Q[l]]>=0||m-j>=m-L) Base[j]+=a[Q[l]];
        }
    }
    ll ans=0;
    rep(i,1,m){
        ans+=Add[i];
        printf("%lld%c",ans+Base[i],i==m?'\n':' ');
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值