树状数组(总结篇)

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\n",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;

}
View Code

 

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) {

    begin[u]=++dfs_clock; dep[u]=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  (我还没搞出来,我太弱了。)

 

其余的题目以后搞出来在加。 (勿转)

 

 后续添加的题目 (线段树都能搞):

 http://codeforces.com/contest/462/problem/E

 

posted @ 2014-07-03 12:12  acvc  阅读(453)  评论(0编辑  收藏  举报