校门外的树 LibreOJ - 10115
原题链接
考察:树状数组
思路:
操作1:读入 l,r 表示在 l 到 r 之间种上一种树,每次操作种的树的种类都不同.
有点像差分,在[l,r]区间内加入同一种树.但是不能全部加1,最后答案是计数种类数.比较直观的想法就是在x = l处+1,x = r处-1.但是当询问[1,r+1]等区间时应该输出1而不是前缀和的0.
将左右端点分开处理,用两个树状数组,第一个数组在x = l处+1,第二个数组在x = r处+1.当询问[l,r]结果是求出[1,r]区间第一个数组的和,[1,l-1]区间第二个数组的和,两个相减.这实际就是括号法.
比较坑的是同一地方可以种很多树.
#include <iostream>
#include <cstring>
using namespace std;
const int N = 50010;
int n,m,tr[2][N];
int lowbit(int x)
{
return x&-x;
}
void add(int t,int k,int x)
{
for(int i=k;i<=n;i+=lowbit(i)) tr[t][i]+=x;
}
int ask(int t,int r)
{
int res = 0;
for(int i=r;i;i-=lowbit(i)) res+=tr[t][i];
return res;
}
int main()
{
scanf("%d%d",&n,&m);
while(m--)
{
int k,l,r; scanf("%d%d%d",&k,&l,&r);
if(k==1) add(0,l,1),add(1,r,1);
else printf("%d\n",ask(0,r)-ask(1,l-1));
}
return 0;
}