E. Let Them Slide 线段树骚操作

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));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值