题目链接: 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−(w−L)),i] 中的最大值。
- 后 L L L 个位置,每个位置上只能取到区间 [ L − i + 1 , m i n ( L , n ) ] [L-i+1,min(L,n)] [L−i+1,min(L,n)] 中的最大值。.
- 中间的任意位置,均可以取到 [ 1 , L ] [1,L] [1,L] 中的最大值。
前两个情况,我们可以用单调栈来维护最大值进行更新。对于第三种情况,可以用差分来维护。
对于出现负数的情况:
因为我们可以将数组推至最左和最右,从而使答案加上 0 0 0 而不至于减少。
前 m − L m-L m−L 个位置和后 m − L m-L m−L 个位置都可以通过将数组移动至最左和最右端来使结果不减少。除了左边的 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;
}