Tsinsen D473 栈

// 内部资料
考场上的思路

根本没思路……师傅们都说是去年的原题——他们在去年这个时候就已经比我强 10 5 倍了 Orz……

思路

师傅的做法太难了,我这种弱智根本听不懂。不过有一种吉司机线段树的做法我倒是听懂了一些。

还是先考虑 Special Instance,若所有询问操作在修改操作之后应该怎么做。我们考虑倒着加入元素,用一棵线段树保存栈底的值。

发现在倒着加入元素时,如果一个元素出现在了栈中,那么它一定比之前的栈底要大,否则他会被之前的栈底弹出。所以如果我们只需要维护栈底的话,我们相当于要做一个区间 checkmax。现在要求栈中元素之和,我们要做的相当于只是多了一个单点加值。

现在考虑所有数据。如何处理修改之前的询问呢?发现,我们在询问时将该位置清零,再继续倒着做就好了。但是如果一个位置有多个询问呢?干脆把所有询问排成一行用线段树维护,而不是维护整个序列。遇到询问时,就把该询问对应的位置清空。做完所有修改后,询问的答案就保存在线段树中。

参考代码
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <cassert>
#include <cctype>
#include <climits>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <map>
#include <set>
#include <bitset>
#include <list>
#include <functional>
using LL = long long;
using ULL = unsigned long long;
using std::cin;
using std::cout;
using std::endl;
using INT_PUT = LL;
INT_PUT readIn()
{
    INT_PUT a = 0;
    bool positive = true;
    char ch = getchar();
    while (!(std::isdigit(ch) || ch == '-'))
        ch = getchar();
    if (ch == '-')
    {
        positive = false;
        ch = getchar();
    }
    while (std::isdigit(ch))
    {
        (a *= 10) -= ch - '0';
        ch = getchar();
    }
    return positive ? -a : a;
}
void printOut(INT_PUT x)
{
    char buffer[20];
    int length = 0;
    if (x < 0) putchar('-');
    else x = -x;
    do buffer[length++] = -(x % 10) + '0'; while (x /= 10);
    do putchar(buffer[--length]); while (length);
    putchar('\n');
}

const int INF = (~(int(1) << (sizeof(int) * 8 - 1))) >> 1;
const int maxn = int(2e5) + 5;
int n, m;
struct Ins
{
    int type;
    int l, r, x;
    void read()
    {
        type = readIn();
        if (type == 1)
        {
            l = readIn();
            r = readIn();
            x = readIn();
        }
        else if (type == 2)
            x = readIn();
    }
} inss[maxn];

#define RunInstance(x) delete new x
struct brute
{
    std::vector<std::vector<int>> stack;

    brute()
    {
        stack.resize(n + 1);
        for (int i = 1; i <= m; i++)
        {
            const Ins &ins = inss[i];
            if (ins.type == 1)
            {
                for (int j = ins.l; j <= ins.r; j++)
                {
                    while (stack[j].size() && stack[j].back() <= ins.x)
                        stack[j].pop_back();
                    stack[j].push_back(ins.x);
                }
            }
            else if (ins.type == 2)
            {
                LL ans = 0;
                for (int j = 0; j < stack[ins.x].size(); j++)
                    ans += stack[ins.x][j];
                printOut(ans);
            }
        }
    }
};
struct work
{
    struct Query
    {
        int pos;
        int idx;
        Query() {}
        Query(int pos, int idx) : pos(pos), idx(idx) {}
        bool operator<(const Query& b) const
        {
            return pos < b.pos;
        }
    };
    int N;
    Query querys[maxn];
    int idx[maxn];
    int poss[maxn];

    class SegTree
    {
        static inline int code(int l, int r)
        {
            return (l + r) | (l != r);
        }
        struct Node
        {
            int major;
            int minor;
            LL sum;
            int lazy;
            void clear()
            {
                major = 0;
                minor = INF;
                sum = 0;
                lazy = 0;
            }
            Node() { clear(); }
        } nodes[maxn * 2];
        int bound;

        int g_L, g_R, g_Pos, g_Val;
        void cover(int l, int r, int val, LL val2)
        {
            Node& t = nodes[code(l, r)];
            if (val <= t.major) return; // note
            t.major = val;
            t.sum += val2;
            t.lazy = std::max(t.lazy, val);
        }
        void pushdown(int l, int r)
        {
            Node& t = nodes[code(l, r)];
            if (t.lazy || t.sum)
            {
                int mid = (l + r) >> 1;
                cover(l, mid, t.lazy, t.sum);
                cover(mid + 1, r, t.lazy, t.sum);
                t.lazy = 0;
                t.sum = 0;
            }
        }
        void update(int l, int r)
        {
            Node& t = nodes[code(l, r)];
            int mid = (l + r) >> 1;
            Node& lc = nodes[code(l, mid)];
            Node& rc = nodes[code(mid + 1, r)];
            if (lc.major == rc.major)
            {
                t.major = lc.major;
                t.minor = std::min(lc.minor, rc.minor);
            }
            else if (lc.major < rc.major)
            {
                t.major = lc.major;
                t.minor = std::min(lc.minor, rc.major);
            }
            else if (lc.major > rc.major)
            {
                t.major = rc.major;
                t.minor = std::min(lc.major, rc.minor);
            }
        }
        void checkmax_(int l, int r)
        {
            Node& t = nodes[code(l, r)];
            if (g_Val <= t.major)
                return;
            if (g_L <= l && r <= g_R && g_Val < t.minor)
            {
                cover(l, r, g_Val, g_Val);
                return;
            }
            pushdown(l, r);
            int mid = (l + r) >> 1;
            if (g_L <= mid) checkmax_(l, mid);
            if (g_R > mid) checkmax_(mid + 1, r);
            update(l, r);
        }
        void reset_(int l, int r)
        {
            if (l == r)
            {
                nodes[code(l, r)].clear();
                return;
            }
            pushdown(l, r);
            int mid = (l + r) >> 1;
            if (g_Pos <= mid) reset_(l, mid);
            else if (g_Pos > mid) reset_(mid + 1, r);
            update(l, r);
        }
        LL query_(int l, int r)
        {
            if (l == r)
            {
                return nodes[code(l, r)].sum;
            }
            pushdown(l, r);
            int mid = (l + r) >> 1;
            if (g_Pos <= mid) return query_(l, mid);
            else if (g_Pos > mid) return query_(mid + 1, r);
        }

    public:
        void SetBound(int b) { bound = b; }
        void checkmax(int l, int r, int val)
        {
            g_L = l;
            g_R = r;
            g_Val = val;
            checkmax_(1, bound);
        }
        void reset(int pos)
        {
            g_Pos = pos;
            reset_(1, bound);
        }
        LL query(int pos)
        {
            g_Pos = pos;
            return query_(1, bound);
        }
    } st;

    work() : N(), idx(), poss()
    {
        for (int i = 1; i <= m; i++)
        {
            if (inss[i].type == 2)
            {
                ++N;
                querys[N] = Query(inss[i].x, i);
            }
        }
        st.SetBound(N);
        std::sort(querys + 1, querys + 1 + N);
        for (int i = 1; i <= N; i++)
            poss[i] = querys[i].pos;
        for (int i = 1; i <= N; i++)
            idx[querys[i].idx] = i;

        for (int i = 1; i <= m; i++)
        {
            if (inss[i].type == 1)
            {
                int newL = std::lower_bound(poss + 1, poss + 1 + N, inss[i].l) - poss;
                int newR = std::upper_bound(poss + 1, poss + 1 + N, inss[i].r) - poss - 1;
                if (newL > newR)
                    inss[i].type = 0;
                else
                {
                    inss[i].l = newL;
                    inss[i].r = newR;
                }
            }
        }

        for (int i = m; i >= 1; i--)
        {
            if (inss[i].type == 1)
            {
                st.checkmax(inss[i].l, inss[i].r, inss[i].x);
            }
            else if (inss[i].type == 2)
            {
                st.reset(idx[i]);
            }
        }

        for (int i = 1; i <= m; i++)
        {
            if (inss[i].type == 2)
            {
                printOut(st.query(idx[i]));
            }
        }
    }
};

void run()
{
    n = readIn();
    m = readIn();
    bool appear = false;
    bool t1 = true;
    for (int i = 1; i <= m; i++)
    {
        inss[i].read();
        if (inss[i].type == 2)
            appear = true;
        if (appear && inss[i].type == 1)
            t1 = false;
    }

    RunInstance(work);
}

int main()
{
#ifndef LOCAL
    freopen("stack.in", "r", stdin);
    freopen("stack.out", "w", stdout);
#endif
    run();
    return 0;
}

注意吉司机线段树的 cover:所有标记都是在满足修改值小于次小值的情况下打上的,因此修改值一定一直小于次小值,不用与次小值进行判断。但是修改值不一定比最小值大,因此必须做判断。在求和标记(sum)下传时,也要先判断修改值是否比最小值大,因为当前的求和标记是在修改成修改值之前打上的,如果最小值比修改值要大,那么所有标记都不会下传。

总结

虽然我想过吉司机的看家法宝,但是我想不到倒着做。唉,我太弱啦!

猜你喜欢

转载自blog.csdn.net/lycheng1215/article/details/80726052