ACM暑假集训日记 17.8.18 树状数组

来源:互联网 发布:中维网络监控 编辑:程序博客网 时间:2024/06/12 22:48

今天继续看了一下树状数组的博客,没有很多的新知识,主要是看了一个离线处理,其他的都是看过的知识点了,但是对于不同的题目使用不尽相同,感觉还是要多看一些题目才能真正的掌握树状数组

先说一下离线处理吧,有时候我们会遇到这样一种问题,给出一个序列,然后给出几个区间查询,求出给定区间内不同数值的和(如果一个数值在这个区间内多次出现也只算一次,不重复加),这时我们采取离线处理的方法,可以想到,在一个区间内,同一个数值只让他出现一次即可,那么我们一开始处理序列的时候,如果a[i]是第一次出现,那么加入树状数组,如果已经出现那么跳过,这样一来,对于任意SUM(R)(即区间[1,R])来说,都是满足要求的,做完1为起点区间的查询以后,我们进行[2,R]的查询,这时候,我们需要消除a[1]对后续的影响,使得我们这个序列好像就是从a[2]开始的,a[1]好像是不存在的,想要达到这个目的,我们需要ADD(1,-a[1])然后在a[1]值下一次出现的地方y位置进行ADD(y,a[1])。这样的话就比较明了了,
我们必须要进行的是按照各个区间的L值进行从小到大的排列,然后依次查询。
当我们查询玩[i,R]后,要进行的操作是
找到a[i]的值出现的下一个位置y(不存在的话是-1),执行ADD(i,-a[i]),ADD(y,a[i])
可以看出我们需要用数组next[i]=j记录a[i]出现的下一个位置j,还需要用hash[h]=i表示值为a[i]出现的第一个位置a[i];

HDU 3874 就是一个很经典的例子

点击打开原题

题意和思路都和上面叙述的差不多,直接发代码了

#include<stdio.h>#include<string.h>#include<algorithm>using namespace std;#define MAXN 50010#define MAXV 1000010#define MAXM 200010long long e[MAXN],ans[MAXM];int a[MAXN],t,n,m;int lowbit(int k){    return k&(-k);}long long SUM(int k){    long long re=0;    while(k)    {        re+=e[k];        k-=lowbit(k);    }    return re;}void ADD(int k,int v){    while(k<MAXN)    {        e[k]+=v;        k+=lowbit(k);    }}struct nodenode{    int head[MAXV],next[MAXV];    void mem()    {        memset(head,-1,sizeof(head));    }    void make(int i,int v)    {        next[i]=-1;        if(head[v]==-1)        head[v]=i;        else        {            int j=head[v];            while(next[j]!=-1)            j=next[j];            next[j]=i;        }    }    int find(int i)    {        return next[i];    }}hhh;struct node{    int l,r,index;    bool operator < (const node &b)const    {        return l<b.l;    }}nodes[MAXM];int main(){    scanf("%d",&t);    while(t--)    {        scanf("%d",&n);        hhh.mem();        memset(e,0,sizeof(e));        for(int i=1;i<=n;i++)        {            scanf("%d",&a[i]);            if(hhh.head[a[i]]==-1)            ADD(i,a[i]);            hhh.make(i,a[i]);        }        scanf("%d",&m);        for(int i=1;i<=m;i++)        {            scanf("%d%d",&nodes[i].l,&nodes[i].r);            nodes[i].index=i;        }        sort(nodes+1,nodes+m+1);        int j=1;        for(int i=1;i<=n;i++)        {            while(nodes[j].l==i)            {                ans[nodes[j].index]=SUM(nodes[j].r);                j++;            }            if(j>m)break;            ADD(i,-a[i]);            int next_ai=hhh.find(i);            if(next_ai!=-1)            ADD(next_ai,a[i]);        }        for(int i=1;i<=m;i++)        printf("%lld\n",ans[i]);    }}

也看了几个树状数组+DP的题目,感觉关键点还是找到DP公式比较关键,

还有的一些题目不太容易能够想到使用树状数组,所以感觉还是要多花点时间练习练习才行。


原创粉丝点击