E. Let Them Slide
题意:就是每行可能有可滑动的一行数据,询问每列的最大和。
题解:我们维护一行数据的每个位置对固定一行位置的贡献,可滑动的数据可以对固定的多个位置有贡献,所以我们维护一颗可以区间更新最大值和最小值的线段树,然后再维护一个sum[o]数组,代表那一段区间的最优解sum[o]当mx[o]==mn[o]时就停止向下更新了,因为如果mx[o]==mn[o]就代表那个区间的最大值都是mx[o]。那么我们查询只要查询到每个位置,然后沿路将值相加。
#include<bits/stdc++.h>
#define ll long long
#define m (l+r)/2
#define ls o*2
#define rs o*2+1
using namespace std;
const int maxn = 1e6+10;
ll sum[maxn*4];
int mn[maxn*4],mx[maxn*4],lz[maxn*4],vis[maxn*4];
int inf = 1e9+1;
vector<int>G;
void add(int o){
if(!vis[o])
G.push_back(o),vis[o] = 1;
}
void pushup(int o,int l,int r){
mn[o] = min(mn[ls],mn[rs]);
mx[o] = max(mx[ls],mx[rs]);
}
void pushdown(int o,int l,int r){
if(lz[o]!=-inf){
mn[ls] = max(mn[ls],lz[o]);
mx[ls] = max(mx[ls],lz[o]);
mx[rs] = max(mx[rs],lz[o]);
mn[rs] = max(mn[rs],lz[o]);
lz[ls] = max(lz[o],lz[ls]);
lz[rs] = max(lz[rs],lz[o]);
add(ls);add(rs);
lz[o] = -inf;
}
}
void up(int o,int l,int r,int ql,int qr,int v)
{
if(ql>qr||mn[o]>=v) return ;
add(o);
if(ql<=l&&qr>=r)
{
if(mn[o]>=v) return ;
if(mx[o]<=v){
mx[o] = mn[o] = lz[o] = v;
return ;
}
pushdown(o,l,r);
if(mn[ls]<v) up(ls,l,m,ql,qr,v);
if(mn[rs]<v) up(rs,m+1,r,ql,qr,v);
pushup(o,l,r);
return ;
}
pushdown(o,l,r);
if(ql<=m) up(ls,l,m,ql,qr,v);
if(qr>m) up(rs,m+1,r,ql,qr,v);
pushup(o,l,r);
}
ll qu(int o,int l,int r,int k)
{
if(l==r) return sum[o];
ll ans = sum[o];
if(k>m) ans += qu(rs,m+1,r,k);
else ans += qu(ls,l,m,k);
return ans;
}
void cul(int o,int l,int r)
{
if(mn[o]==mx[o]){
sum[o] += mx[o];
return ;
}
cul(ls,l,m);
cul(rs,m+1,r);
}
int main()
{
int n,w;scanf("%d%d",&n,&w);
for(int i=0;i<=w*4;i++)
mx[i] = mn[i] = lz[i] = -inf;
for(int i=1;i<=n;i++){
int l,p;
scanf("%d",&l);
for(int j=1;j<=l;j++)
{
scanf("%d",&p);
up(1,1,w,j,w-l+j,p);
}
if(l<w){
up(1,1,w,1,w-l,0);
up(1,1,w,l+1,w,0);
}
cul(1,1,w);
for(auto v:G) mn[v] = mx[v] = lz[v] = -inf,vis[v] = 0;
G.clear();
}
for(int i=1;i<=w;i++)
printf("%lld\n",qu(1,1,w,i));
}