『题解』洛谷P2357 守墓人
Portal
Portal1: Luogu
Description
在一个荒凉的墓地上有一个令人尊敬的守墓人,他看守的墓地从来没有被盗过, 所以人们很放心的把自己的先人的墓安顿在他那守墓人能看好这片墓地是必然而不是偶然.....
因为....守墓人懂风水。他把墓地分为主要墓碑和次要墓碑,主要墓碑只能有\(1\)个, 守墓人把他记为\(1\)号, 而次要墓碑有\(n-1\)个,守墓人将之编号为\(2, 3 \dots n\),所以构成了一个有\(n\)个墓碑的墓地。
而每个墓碑有一个初始的风水值,这些风水值决定了墓地的风水的好坏,所以守墓人需要经常来查询这些墓碑。
善于运用风水的守墓人,通过一次次逆天改命,使得自己拥有了无限寿命,没人知道他活了多久。
这天,你幸运的拜访到了他,他要求你和他共同见证接下来几年他的战果,但不过他每次统计风水值之和都需要你来帮他计算,算错了他会要你命QAQ
,风水也不是不可变,除非遭遇特殊情况,已知在接下来的\(2147483647\)年里,会有\(n\)次灾难,守墓人会有几个操作:
-
将\([l,r]\)这个区间所有的墓碑的风水值增加\(k\);
-
将主墓碑的风水值增加\(k\);
-
将主墓碑的风水值减少\(k\);
-
统计\([l,r]\)这个区间所有的墓碑的风水值之和;
-
求主墓碑的风水值。
上面也说了,很多人会把先人的墓安居在这里,而且守墓人活了很多世纪,墓碑的数量会多的你不敢相信,守墓人和善的邀请你帮他完成这些操作,要不然哪天你的旅馆爆炸了,天上下刀子\(\dots \dots\)为了活命,还是帮他吧。
Input
第一行,两个正整数\(n, f\)表示共有\(n\)块墓碑,并且在接下来的\(2147483647\)年里,会有\(f\)次世界末日;
第二行,\(n\)个正整数,表示第\(i\)块墓碑的风水值;
接下来\(f\)行,每行都会有一个针对世界末日的解决方案,如题所述,标记同题。
Output
输出会有若干行,对\(4\)和\(5\)的提问做出回答。
Sample Input
5 7
0 0 0 0 0
1 1 5 1
1 1 3 3
2 3
3 1
4 1 5
2 1
5
Sample Output
16
7
Hint
\(20\%\)的数据满足:\(1 \le n \le 100\);
\(50\%\)的数据满足:\(1 \le n \le 6000\);
\(100\%\)的数据满足:\(1 \le n, f \le 2 \times 10^{5}\);
Solution
这题树状数组。
用c1[]
来维护和,c2[]
来维护积。
对于询问\([x, y]\)区间,可以用query(y) - query(x - 1) + (x == 1) * 主墓碑的风水值
来求值,其中query(y) - query(x - 1)
类似于前缀和,(x == 1) * 主墓碑的风水值
表示\([x, y]\)区间包不包含主墓碑,如果包含则加上主墓碑单独维护的。
其中主墓碑需要单独维护。
Code
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const int MAXN = 500005;
int n, m, last;
LL first_value, c1[MAXN], c2[MAXN];
inline int lowbit(int x) {//树状数组专属操作
return x & -x;
}
inline void update(int x, LL val) {//更新
for (LL i = x; i <= n; i += lowbit(i)) {
c1[i] += val;
c2[i] += val * x;
}
}
inline LL query(int x) {//查询
LL ret = 0;
for (LL i = x; i; i -= lowbit(i))
ret += (x + 1) * c1[i] - c2[i];
return ret;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) {
int x;
scanf("%d", &x);
update(i, x - last);
last = x;
}
while (m--) {
int opt, x, y;
LL val;
scanf("%d", &opt);
if (opt == 1) {
scanf("%d%d%lld", &x, &y, &val);
update(x, val);
update(y + 1, -val);
} else
if (opt == 2) {
scanf("%lld", &val);
first_value += val;
} else
if (opt == 3) {
scanf("%lld", &val);
first_value -= val;
} else
if (opt == 4) {
scanf("%d%d", &x, &y);
printf("%lld\n", query(y) - query(x - 1) + (x == 1) * first_value);
} else printf("%lld\n", query(1) + first_value);//统一维护的加单独维护的
}
return 0;
}