AT2289 [ARC067D] Yakiniku Restaurants

传送门

(本来是联系决策单调的,但似乎搞着搞着变成杂题了?)


思路

我们有一个朴素的 \(O(n^2m)\) 的做法,就是枚举左右端点,然后更新每种票贡献的最大值

我们要想方法将一个 \(n\)\(m\) 给优化掉

考虑用扫描线的方法:

我们枚举右端点 \(r\),设 \(Mx[l]\) 为当左端点为 \(l\) 时的最大答案

当我们向右移动时,我们可以发现,对于每种票 \(i\),如果有 \(w[l][i]<w[r][i]\),那么代表 \(Mx[l]\) 一定可以被更新,而且这一定是连续一段的,即为连续一段区间 \([l,r]\),满足 \(w[l - 1][i]>w[r][i]\text{ and }\forall l\le x\le r,\ w[x][i]\le w[r][i]\)

但这样依旧可能被精心构造的数据卡成 \(O(n^2m)\)

但我们还可以发现,对于一段区间 \([x,y]\),如果 \(\forall x\le z < y\),有 \(w[z][i]\le w[y][i]\),那么说明在之前这段区间第 \(i\) 种票都已经被更新为 \(w[y][i]\) 了,那么我们对于这种区间就可以直接区间加(线段树实现)

因此我们只需要用单调栈记录这些关键点,如果一个点被访问到,那么这个点就会被弹出,因此单调栈的复杂度\(O(nm)\)

总复杂度就为 \(O(nm\log n)\)


#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
inline int reads()
{
    int sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
int n, m, w[5005][205], p[5005][205]; LL ans, dis[5005];
namespace Seg_Tree
{
    #define ls (now << 1)
    #define rs ((now << 1) | 1)
    LL tr[20005], tag[20005];
    inline void down(int now, int l, int r)
    {
        if(tag[now])
        {
            tr[ls] += tag[now], tr[rs] += tag[now];
            tag[ls] += tag[now], tag[rs] += tag[now];
            tag[now] = 0;
        }
    }
    void add(int now, int l, int r, int to)
    {
        if(l == r)
        {
            for(int i = 1; i <= m; i++) tr[now] += 1ll * w[to][i];
            tr[now] += dis[to];
            return;
        }
        int mid = (l + r) >> 1; down(now, l, r);
        if(to <= mid) add(ls, l, mid, to);
        else add(rs, mid + 1, r, to);
        tr[now] = std::max(tr[ls], tr[rs]);
    }
    void modify(int now, int l, int r, int L, int R, LL add)
    {
        if(L <= l && r <= R)
        {
            tag[now] += add;
            tr[now] += add;
            return;
        }
        int mid = (l + r) >> 1; down(now, l, r);
        if(L <= mid) modify(ls, l, mid, L, R, add);
        if(mid < R) modify(rs, mid + 1, r, L, R, add);
        tr[now] = std::max(tr[ls], tr[rs]);
    }
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = reads(), m = reads();
    for(int i = 2; i <= n; i++)
        dis[i] = dis[i - 1] + reads();
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            w[i][j] = reads();
    for(int i = 1; i <= n; i++)
    {
        Seg_Tree::add(1, 1, n, i);
        for(int j = 1; j <= m; j++)
        {
            int now = i - 1;
            while(now && w[now][j] <= w[i][j])
            {
                Seg_Tree::modify(1, 1, n, p[now][j], now, w[i][j] - w[now][j]);
                now = p[now][j] - 1;
            }
            p[i][j] = now + 1;
        }
        ans = std::max(ans, Seg_Tree::tr[1] - dis[i]);
    }
    printf("%lld", ans);
    return 0;
}
posted @ 2022-05-10 21:33  zuytong  阅读(26)  评论(0编辑  收藏  举报