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;
}