LuoguP7870 「Wdoi-4」兔已着陆 题解

Content

对一个栈执行如下操作:

  • 1 l r:依次向栈里面弹入 \(l,l+1,\dots,r-1,r\)
  • 2 k:依次从栈里面弹出 \(k\) 个数,并求出所有弹出的数的和。

数据范围:\(1\leqslant n\leqslant 10^5\)\(0\leqslant l\leqslant r\leqslant 10^6\)\(1\leqslant k\leqslant 10^{12}\)

Solution

显然,一个一个去暴力弹入弹出显然是不现实的。那么怎么办?一段一段去弹入弹出即可。

首先对于操作 \(1\),我们直接把 \([l,r]\) 这一段当做一个元素直接弹入栈里面即可。

关键在于操作 \(2\),首先不难发现对于 \([l,r]\) 这一整段,其含有的数的个数为 \(r-l+1\),和为 \(\frac{(l+r)(r-l+1)}2\)。因此,对于能够取出的整段,我们直接暴力不断取出,直到 \(k\) 不够取出整段为止。

那么取完整段之后如果 \(k\) 不为 \(0\) 呢?我们考虑把原来的一段取出并拆成两段:一段是 \([l,r-k+1]\),另一段是 \([r-k+1,r]\)。我们要取出来的就是 \([r-k+1,r]\) 这一段,并且不难求出这一段的和为 \(\frac{(2r-k+1)k}2\)。把右半段取出,统计入总和并把左半段作为一个新的整段弹入栈里面就好了。

那么这道题目就做完了,具体实现可以手写栈也可以直接用 stl 中的 stack 容器。

Code

为了偷懒直接 #define int ll 了(

namespace Solution {
    #define int ll
    struct node {int l, r;};
    stack<node> q;

    iv Main() {
        MT {
            int op, x, y; read(op);
            if(op == 1) read(x, y), q.push((node){x, y});
            else {
                read(x);
                ll ans = 0;
                while(!q.empty() && x >= (q.top().r - q.top().l + 1)) x -= q.top().r - q.top().l + 1, ans += 1ll * (q.top().l + q.top().r) * (q.top().r - q.top().l + 1) / 2, q.pop();
                if(x) {
                    node cur = q.top(); q.pop();
                    ans += 1ll * (cur.r - x + 1 + cur.r) * x / 2;
                    cur.r -= x, q.push(cur);
                }
                write(ans), puts("");
            }
        }
        return;
    }
    #undef int
}
posted @ 2021-12-15 21:20  Eason_AC  阅读(160)  评论(0)    收藏  举报