1208E Let Them Slide

题目大意

给你n个序列,每个一行

每个序列是可以左右移动的

对于每一列问在随意左右移动这些序列的情况下

这一列的每个数的和最大是多少

分析

对于每个序列分为两种情况

[1]长度小于长度的一半

我们发现这种情况下一定是两头长度为k的地方只能考虑序列开头/结尾的前k个

于是我们直接维护开头和结尾前k个的最大值

对于每一个最大值的影响区间我们差分处理

[2]长度大于等于长度的一半

对于全长的每一个点我们可以轻松算出它对应序列上的哪些点

直接st表求最大值即可

仍然使用差分的方式维护

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
#define li long long
int n,w,m,lg[1000100],st[2000100][23];
li ans[1000100];
inline int q(int le,int ri){
    int k=lg[ri-le+1];
    return max(st[le][k],st[ri-(1<<k)+1][k]);
}
int main(){
    int i,j,k;
    scanf("%d%d",&n,&w);
    for(i=2;i<=1000000;i++)lg[i]=lg[i>>1]+1;
    for(int _=1;_<=n;_++){
      scanf("%d",&m);
      for(i=1;i<=m;i++)scanf("%d",&st[i][0]);
      for(i=1;i<=20;i++)
        for(j=1;j<=m;j++)
          st[j][i]=max(st[j][i-1],st[j+(1<<(i-1))][i-1]);
      if(2*m>=w){
          for(i=1;i<=w;i++){
            int le=m-w+i,ri=i,v=q(max(le,1),min(ri,m));
            if(le<1||ri>m)v=max(v,0);
            ans[i]+=(li)v;
            ans[i+1]-=(li)v;
          }
      }else {
          int v1=0,v2=0;
          for(i=1;i<=m;i++){
            v1=max(v1,st[i][0]);
            v2=max(v2,st[m-i+1][0]);
            ans[i]+=(li)v1;
            ans[i+1]-=(li)v1;
            ans[w-i+1]+=(li)v2;
            ans[w-i+2]-=(li)v2;
            }
        ans[m+1]+=(li)v1;
        ans[w-m+1]-=(li)v1;
      }
    }
    for(i=1;i<=w;i++){
      ans[i]+=ans[i-1];
      printf("%lld ",ans[i]);
    }
    puts("");
    return 0;
}
posted @ 2019-08-26 18:42  水题收割者  阅读(467)  评论(0编辑  收藏  举报