树状数组学习例题(续)

1.洛谷p1972 离线+树状数组

转载自:https://www.luogu.org/blog/Samle/solution-p1972

求m个区间内有多少个不同的数,看到数据N <= 500000,M <= 200000,就不可能是暴力了,那么怎么求m个区间内不同数的个数呢?可以用树状数组。首先用一个used数组来对树状数组初始化,对于每个数,如果出现过就将其存入树状数组中。

    for(int i=1;i<=n;i++)
    {
        if(used[a[i]]==0)
        {
            used[a[i]]=1;
            add(i,1);
        }
    }

之后构建一个next数组将第i个数下一次出现的最早位置存入:

for(int i=n;i>0;i--)
        {
            if(!used[a[i]])
                nex[i]=n+1;
            else
            {
                nex[i]=used[a[i]];
            }
            used[a[i]]=i;
        }

用树状数组求和之前首先将求和区间按照左端点由小到大排序,在求和时,对于l到r这个区间,如果发现在这段区间前面出现过这段区间中的数,即next数组有值,就将出现过的点在树状数组上更新,将前面出现过的值抵消掉,最后答案就是ans[i]=ask(r)-ask(l-1)

for(int i=1;i<=m;i++)
    {
        for(;j<q[i].x;j++)add(nex[j],1);
        ans[q[i].id]=ask(q[i].y)-ask(q[i].x-1);
    }

2.校门外的树(树状数组+括号序列):

转载自:https://www.cnblogs.com/ECJTUACM-873284962/p/7060158.html

括号序列:

假设有一个长度为10的数轴,讲区间[2,5]种树,即在2处放左括号,5处放右括号,查询[3,5]区间即求5(包括5)之前有多少左括号跟3(不包括3)之前有多少右括号。相减即为有多少相同区间。用树状数组优化求和时间。

代码:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include<algorithm>
#include <string.h>
using namespace std;
const int M=51000;
int sum1[M],sum2[M],n;
void add(int p,int a[])
{
    for(int i=p;i<=n;i+=i&-i)
        {
          a[i]++;
        }
}
int ask(int p,int a[])
{
    int ans=0;
    for(int i=p;i>0;i-=i&-i)
     ans+=a[i];
    return ans;
}

int main()
{
    int m;
    scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            int o,x,y;
            scanf("%d%d%d",&o,&x,&y);
            if(o==1)
            {
                 add(x,sum1);
                 add(y,sum2);
            }
            else
            {
                 printf("%d\n",ask(y,sum1)-ask(x-1,sum2));
            }

        }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Black__wing/article/details/81290996