问题
TLE代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int b[N];
void add(int l, int r, int d)
{
b[r+1] -= d;
b[l] += d;
}
int query(int x)
{
int retval = 0;
for(int i = 1; i <= x; i++)
{
retval += b[i];
}
return retval;
}
int main()
{
int n, m;
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
int tmp;
scanf("%d", &tmp);
add(i, i, tmp);
}
for(int i = 1; i <= m; i++)
{
char op;
scanf(" %c", &op);
if(op == 'C')
{
int l, r, d;
scanf("%d%d%d", &l, &r, &d);
add(l, r, d);
}
else if(op == 'Q')
{
int x;
scanf("%d", &x);
printf("%d\n", query(x));
}
}
return 0;
}
假设n=1e5, m=1e5, 操作全是Q,则操作次数达到
1
0
10
10^{10}
1010
正确代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
int b[N];
int n, m;
int lowbit(int x)
{
return x & (-x);
}
void update(int x, int d)
{
for(; x <= n; x += lowbit(x))
{
b[x] += d;
}
}
int query(int x)
{
int retval = 0;
for(; x >= 1; x -= lowbit(x))
{
retval += b[x];
}
return retval;
}
int add(int l, int r, int d)
{
update(l, d);
update(r+1, -d);
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
int tmp;
scanf("%d", &tmp);
add(i, i, tmp);
}
for(int i = 1; i <= m; i++)
{
char op;
scanf(" %c", &op);
if(op == 'C')
{
int l, r, d;
scanf("%d%d%d", &l, &r, &d);
add(l, r, d);
}
else if(op == 'Q')
{
int x;
scanf("%d", &x);
printf("%d\n", query(x));
}
}
return 0;
}
思考
对比前缀和、差分和树状数组
算法 | 目的 | 单次操作复杂度 |
---|---|---|
前缀和 | 快速求子段和 | 区间求和 O ( 1 ) O(1) O(1) \; 区间修改 O ( n ) O(n) O(n) |
差分 | 快速子段修改 | 区间修改 O ( 1 ) O(1) O(1) \; 单点查询 O ( n ) O(n) O(n) |
树状数组 | 均衡上述目的 | 单点修改 O ( l o g n ) O(logn) O(logn) \; 区间求和 O ( l o g n ) O(logn) O(logn) |
差分+树状数组 | 均衡差分 | \; 单点查询 O ( l o g n ) O(logn) O(logn) |
区间修改指的是区间加减
能够区间求和就自然可以单点查询
差分+树状数组的思路:树状数组维护差分序列,通过树状数组的单点修改操作进行差分的区间修改,通过树状数组的区间求和优化差分的单点查询
总结树状数组
求区间和要想到 (1)
单点修改要想到 (3)