by zyq / 2014 / 7
树状数组何其牛逼只能这么形容啊。
首先,网上好多对树状数组基础知识讲解的在这就不细讲了。可以参考
http://blog.csdn.net/shahdza/article/details/6314818 (内容比较老了)
下面总结一下树状数组的题型 :
1.最简单的单点更新区间查询 。 其实这个是树状数组的精髓了,不过入门的时候都会做些简单题找感觉吧。
下面附上简单题找感觉,动脑子的题后面贴。
http://poj.org/problem?id=2299
http://poj.org/problem?id=2352
http://poj.org/problem?id=1195 (二维的裸题呢)
http://poj.org/problem?id=2481
http://poj.org/problem?id=2029 (又是一个二维的裸题)
2.插线问点问题:
这是一个经典问题,题目描述为:给连续区间内同时给出某种操作。 然后询问单点的值是多少。
例如:把区间【a,b-1】内的数同时加上v,然后询问c点的值。 我们可以这么操作: 在更新a处的值(加上v) ,然后更新b 处的值(减去v)
这样任何落在区间【a,b-1】的点都会加上1不在区间上的点都不变。
可以参考上图
下面附上一道练习:
http://poj.org/problem?id=2155
http://acm.hdu.edu.cn/showproblem.php?pid=3584 (三维问题)
http://acm.hdu.edu.cn/showproblem.php?pid=4267 (维护多颗树状数组问题)
http://acm.hdu.edu.cn/showproblem.php?pid=5057 (维护多颗树状数组问题)
插线问点是离线树状数组的基础问题哦。。
3:区间修改区间查询的问题 。
这个问题显得不是那么重要(用线段树组搞很简单的)。用树状数组搞就是在深入理解下他的用法。
我们可以维护两个树状数组。
具体可以搞出 http://poj.org/problem?id=3468
我的代码:
by cao ni ma
hehe
*/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <vector>
#include <list>
#include <queue>
#include <stack>
using namespace std;
typedef long long LL;
const int MAX = 100000+10;
const int inf = 0x3f3f3f3f;
LL C[2][MAX];
int n,m;
int lowbit(int x) {return x&-x;}
void add(LL *C,int x,LL val) {
for(int i=x;i<=n;i+=lowbit(i)) C[i]+=val;
}
LL sum(LL *C,int x) {
LL res=0;
for(int i=x;i;i-=lowbit(i)) res+=C[i];
return res;
}
LL val[MAX]; char str[3];
int main()
{
//freopen("in","r",stdin);
//freopen("out","w",stdout);
LL a,b,c;
while(scanf("%d %d",&n,&m)==2) {
memset(C,0,sizeof(C));
for(int i=1;i<=n;i++) {
scanf("%I64d",&val[i]);
add(C[0],i,val[i]);
}
for(int i=0;i<m;i++) {
scanf("%s",str);
if(str[0]=='Q') {
scanf("%I64d %I64d",&a,&b);
LL res=sum(C[1],b)*b-sum(C[1],a-1)*(a-1)+sum(C[0],b)-sum(C[0],a-1);
printf("%I64d ",res);
}
else {
scanf("%I64d %I64d %I64d",&a,&b,&c);
add(C[1],a,c);
add(C[1],b+1,-c);
add(C[0],a,-c*(a-1));
add(C[0],b+1,b*c);
}
}
}
return 0;
}
4:离线处理问题;
这种类型的问题主要是考虑到 查询之间存在某种影响,为了消除这些影响我们需要按某些顺序来处理这些查询。
最常见的就是按区间右端点(或左端点)排序从左向右扫描(从右向左)。具体的可以搞几道题就明白了。
http://acm.hdu.edu.cn/showproblem.php?pid=3333
http://acm.hdu.edu.cn/showproblem.php?pid=3874 (经典题,求区间不出复数字的个数)
http://acm.hdu.edu.cn/showproblem.php?pid=4630 (多校赛很不错的题目) (其实相当于是括号匹配,只不过这里括号变成了因子)。
http://acm.hdu.edu.cn/showproblem.php?pid=4638
接下来是我在cf上千辛万苦找的题。
http://codeforces.com/problemset/problem/369/E (和上面的操作看似相反其实可以转化。)
http://codeforces.com/problemset/problem/301/D
http://codeforces.com/problemset/problem/220/B (很经典的题目)
5.最后一个经典问题就是树形结构编程线型
这类问题一般是修改某个节点的子孙节点然后问修改之后的状态。 我们利用深搜吧记录下第一次访问v的位置和最后一次访问v的位置 这样
树形结构就能搞成线性操作了。
详见dfs片段。
int dfs(int u,int pre,int d) {
for(int i=0;i<G[u].size();i++) {
int v=G[u][i];
if(v!=pre) dfs(v,u,1-d);
}
end[u]=dfs_clock;
}
下面是几道类似的题目:
http://acm.hdu.edu.cn/showproblem.php?pid=4358
http://poj.org/problem?id=3321
http://codeforces.com/problemset/problem/396/C
http://codeforces.com/problemset/problem/383/C
能搞出上面的题目基本上算是入门了饿。
接下来可以搞搞其他的数据结构会有更深的认识。
下面列出几道树状数组的神题(我认为)
http://codeforces.com/problemset/problem/341/D (这道题是二维操作维护四颗树状数组,比赛时吧二维线段树都卡掉了)
http://codeforces.com/problemset/problem/276/E (我还没搞出来,我太弱了。)
其余的题目以后搞出来在加。 (勿转)
后续添加的题目 (线段树都能搞):