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
}

浙公网安备 33010602011771号