校门外的树 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;
}
posted @ 2021-05-16 15:01  acmloser  阅读(58)  评论(0编辑  收藏  举报