题目链接:1007 Naive Operations
题目大意
两个数组a和b, 长度为n,a一开始全都是0,b里面是1-n的排列
有两两种操作
add l, r : 将a数组[l, r]内所有元素+1
query l, r : 求
∑ri=l⌊aibi⌋
∑
i
=
l
r
⌊
a
i
b
i
⌋
思路
线段树区间查询区间内的最大值以及最大值的位置(如果有多个返回最左边的)
区间修改将区间内所有数字加上或减去一个数字
再使用另一个线段树或者树状数组保存实际答案
因为答案是向下取整的, 所以我们只有在a[i]增加到b[i]时答案的值才会增加1
一开始线段树中位置i的值是-b[i],更新时,将[l, r]区间每个元素+1,然后查询[l, r]最大值及其位置p,如果最大值为0,就说明有a[i]等于b[i]了, 那么将这个最大值的位置的值重新设置为-b[i], 将存储答案的线段树i位置+1,再查询[p, r]最大值,重复上面步骤
由于每次add平均最多会有一个点的答案+1,所以不会超时, 复杂度 O(qlogn) O ( q l o g n )
因为没有lazy标记的线段树的模版, 而且很久没写过线段树了, 比赛的时候wa了好几发, 最后发现问题在pushDown上面, 这份代码就当作模版了
代码
1007 1045MS 5264K G++
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1e5 + 100, mod = 1e9 + 7, inf = 0x3f3f3f3f;
typedef pair<int, int> P;
typedef long long ll;
#define ls l, m, rt<<1
#define rs m+1, r, rt<<1|1
#define defm int m = (l+r)>>1
int b[maxn];
int q, n;
//线段树维护最左边的最大值
int mx[maxn << 2], mx_id[maxn << 2], col[maxn << 2];//最大值, 最大值下标, 懒惰标记
void pushUp(int rt)
{
if (mx[rt << 1] >= mx[rt << 1 | 1])
{
mx[rt] = mx[rt << 1];
mx_id[rt] = mx_id[rt << 1];
}
else
{
mx[rt] = mx[rt << 1 | 1];
mx_id[rt] = mx_id[rt << 1 | 1];
}
}
void pushDown(int rt)
{
if (col[rt])
{
col[rt << 1] += col[rt];
col[rt << 1 | 1] += col[rt];//这里是+=不是=
mx[rt << 1] += col[rt];//这里+=col[rt]而不是col[rt<<1]
mx[rt << 1 | 1] += col[rt];
col[rt] = 0;
}
}
void build(int l, int r, int rt)
{
col[rt] = 0;
if (l == r)
{
mx[rt] = -b[l];
mx_id[rt] = l;
return ;
}
int m = (l + r) >> 1;
build(ls);
build(rs);
pushUp(rt);
}
void update(int L, int R, int c, int l, int r, int rt)
{
if (L <= l && r <= R)
{
col[rt] += c;
mx[rt] += c;
return ;
}
pushDown(rt);
int m = (l + r) >> 1;
if (L <= m) update(L, R, c, ls);
if (R > m) update(L, R, c, rs);
pushUp(rt);
}
P query(int L, int R, int l, int r, int rt)//first最大值, second下标
{
if (L <= l && r <= R)
{
return P(mx[rt], mx_id[rt]);
}
pushDown(rt);
int m = (l + r) >> 1;
P t1 = P(-inf, -inf), t2 = P(-inf, -inf);
if (L <= m) t1 = query(L, R, ls);
if (m < R) t2 = query(L, R, rs);
if (t1.first >= t2.first) return t1;
else return t2;
}
//保存答案的树状数组, 直接用模版了
ll bit[maxn];
inline int lowbit(int x) { return x & (-x); }
inline void init(int n) { memset(bit, 0, sizeof(int) * (n + 10)); }
ll sum(int k)
{
return k <= 0 ? 0 : bit[k] + sum(k - lowbit(k));
}
void update(int p, int x)
{
for ( ; p <= n; p += lowbit(p)) bit[p] += x;
}
inline ll query(int l, int r) { return sum(r) - sum(l - 1); }
int main()
{
while (scanf("%d%d", &n, &q) == 2)
{
for (int i = 1; i <= n; ++i) scanf("%d", b + i);
build(1, n, 1);
init(n);
char op[10];
int l, r;
for (int i = 0; i < q; ++i)
{
scanf("%s%d%d", op, &l, &r);
if (op[0] == 'a')
{
update(l, r, 1, 1, n, 1);
while (l <= r)
{
P t = query(l , r, 1, n, 1);
if (t.first >= 0)//有a[i]等于0了,答案加1
{
update(t.second, 1);
update(t.second, t.second, -b[t.second], 1, n, 1);
}
else break;
l = t.second;
}
}
else
{
printf("%lld\n", query(l, r));
}
}
}
return 0;
}