P3374 【模板】树状数组 1
声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。
题目描述
如题,已知一个数列,你需要进行下面两种操作:
将某一个数加上 (x)
求出某区间每一个数的和。
前言
感觉最近学东西真的太快了...挺多都是囫囵吞枣,如果没有深入理解的话,做题就很难想到。这也是为什么我平时学得挺开心,一旦做题就懵逼的一大原因吧。
(\)
(\)
(Solution)
树状数组方法就不说了
关于 (CDQ),其实就是一个按照时间分治的东西(当然了,在某些题目里可以把某些条件转化为时间)
例如对于这道题来说,每个操作都是按照时间表的顺序进行的,前面的修改可能会影响后面的查询,但前面的查询不可能被后面的修改影响
那么我们可以尝试着把所有操作分成两个子问题,解决完每个子问题后,我们只需统计左区间对右区间的影响即可
如何统计这个影响呢?
我们注意到一个问题,对于某个询问操作 (a),有一个修改操作 (b) 对它产生影响,那么 (b) 除了在时间表上的位置要比 (a) 靠前之外,(b) 所修改的点的位置也应该在 (a) 所要查询的区间内的
对于很多个区间,似乎不太好统计答案...于是我们可以把一个区间变为两个前缀和相减,把原来的一个询问操作 (a) 变为两个求前缀和的操作
那么可以把左右区间的所有操作排个序,按照位置枚举各个操作,若修改操作 (b) 对某个前缀和操作 (a) 产生了影响,那么 (b) 一定对 (a) 即后面的所有前缀和操作都产生了影响,就可以用一个 (sum) 来累加产生的影响即可(感觉有点糊,具体看代码)
快排的时间是 (O(nlog{n})) 的,则总复杂度会有两个 (log) ,会被卡掉
但是我们可以发现,由于是递归实现,那么左区间和右区间一定都是有序的,就可以用归并排序,一边归并一边计算贡献
完结撒花✿✿ヽ(°▽°)ノ✿
(\)
(\)
(Code)
#include<bits/stdc++.h>
#define F(i, x, y) for(int i = x; i <= y; ++ i)
#define ll long long
using namespace std;
ll read();
const int N = 2e6 + 5;
int n, m, cnt, tot;
ll ans[N];
struct order{
ll pos, k, val;//pos 为位置,k 为操作类型,val 为修改的值 / 询问的编号
}p[N], tmp[N];
void CDQ(int l, int r)
{
if(l == r) return;
ll mid = (l + r) >> 1;
CDQ(l, mid), CDQ(mid + 1, r);
ll sum = 0, i = l, j = mid + 1, o = l - 1;//归并的两个指针
while(i <= mid && j <= r)
if(p[i].pos <= p[j].pos)
{
if(p[i].k == 1) sum += p[i].val;//如果是左区间的,那么就累加影响
tmp[++ o] = p[i ++];
}
else
{
if(p[j].k == 2) ans[p[j].val] -= sum;//此时累加的影响一定会对此处前缀和产生影响,因为那些操作的时间和位置都在此处之前
if(p[j].k == 3) ans[p[j].val] += sum;//同上
tmp[++ o] = p[j ++];
}
while(i <= mid) tmp[++ o] = p[i ++];
while(j <= r)
{
if(p[j].k == 2) ans[p[j].val] -= sum;
if(p[j].k == 3) ans[p[j].val] += sum;
tmp[++ o] = p[j ++];
}
F(i, l, o) p[i] = tmp[i];//最后整体归并完成
}
int main()
{
n = read(), m = read(), tot = n;
F(i, 1, n) p[i].val = read(), p[i].pos = i, p[i].k = 1;//直接把原数组也看成一次操作
for(int i = 1, op; i <= m; ++ i)
{
op = read();
if(op == 1)
p[++ tot].k = 1, p[tot].pos = read(), p[tot].val = read();
else
{
p[++ tot].k = 2, p[tot].pos = read() - 1, p[tot].val = ++ cnt;
p[++ tot].k = 3, p[tot].pos = read(), p[tot].val = cnt;
}
}
CDQ(1, tot);
F(i, 1, cnt) printf("%lld
", ans[i]);
return 0;
}
ll read()
{
int x = 0, f = 1;
char c = getchar();
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}