更多文章可以在本人的个人小站:https://kaiserwilheim.github.io 查看。
转载请注明出处。
思路
这道题想让我们维护一个数列,支持对其的修改与询问。
题目给出了两种操作:
U k a
将序列中第 k k k 个数修改为 a a a。Z c s
在这个序列上,每次选出 c c c 个正数,并将它们都减去 1 1 1,询问能否进行 s s s 次操作。
回答询问的思路是这个样子的:
我们假设在这个数列里面,KaTeX parse error: Can't use function '\(' in math mode at position 1: \̲(̲ 0,s \) 范围内的数有 x x x 个,KaTeX parse error: Undefined control sequence: \[ at position 1: \̲[̲ s, \infty \) 范围内的数有 y y y 个。
如果 x + y < c x+y < c x+y<c 的话就绝对不行,直接返回 NIE
。
我们贪一下心,反正题目询问的是可行性,不如就先让着 y y y 个数先顶上。
如果这 y y y 个数都顶上去之后就可以完成任务,甚至还有些富余,就可以直接返回 TAK
。
而如果 y < c y < c y<c,那么我们就需要继续往下讨论。
我们考虑让剩下的数顶上去。
如果这些数字的和小于 s × c s \times c s×c,那就绝对完不成任务。
反之则一定完得成任务。
QED.
所以,我们需要维护两个信息,一是大于某个数的数有多少个,二是大于某个数的所有数之和。
我们可以使用树状数组。
但是我不会,所以就用动态开点权值线段树了。
我们考虑结构体里面存什么:
首先我们需要存区间左右端点(按个人情况)和左右儿子。
我们还需要维护区间内数的个数。
为了不多写数据结构,我也将第二个要求写进了线段树里面。
于是我的结构体长的是这个样子:
struct SegTree
{
int l, r;
int ls, rs;
ll sum;
ll tot;
}
其中 sum
维护的是当前区间内数的个数,tot
维护的是当前区间内所有数的和。两者同时维护,但是查询的时候是分开的。
线段树部分代码:
struct SegTree
{
int l, r;
int ls, rs;
ll sum;
ll tot;
}tr[N << 3];
int idx;
void segadd(int p, int pos, ll k)
{
if(tr[p].l == tr[p].r)
{
tr[p].sum += k;
tr<