校门外的树

Description

校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的…… 
如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作: 
K=1,读入l,r表示在l~r之间种上的一种树 
K=2,读入l,r表示询问l~r之间能见到多少种树 
(0<l,r)

Input

第一行n,m表示道路总长为n,共有m个操作 
接下来m行为m个操作 

Output

对于每个k=2输出一个答案

Sample Input

5 4
1 1 3
2 2 5
1 2 4
2 3 5

Sample Output

1
2

Hint

20%的数据保证,n,m<=100 
60%的数据保证,n <=1000,m<=50000 
100%的数据保证,n,m<=50000


【分析】

        这道题是对区间进行操作,自然而然就想到了线段树。但是发现比较麻烦的一点出现了,他是查询每段区间各有多少种树。所以线段树是行不通的。于是就想到了树状数组。我们可以开两个树状数组,分别记录每次区间修改的右端点和左端点

        那么每次查询的答案就是(总的种类数-查询区间的左端点 左边的 修改区间的右端点数-查询区间的右端点 右边的 修改区间的左端点数)但是注意那个数不要包含当前查询区间的端点。这样说有一点绕,我们来举个例子。

        设有这样一组数据:添加  (1)1~3    (2) 2~4   (3)3~5   (4)5~6 我们顺序编号树的编号,那么得到这样一列树。

位置123456
第一次修改111   
第二次修改 222  
第三次修改  333 
第四次修改    44
        如果我们查询4~5的树的种类的话,显然答案应该是3种,这三种树的编号为2,3,4;

        这个答案是怎么求出来的呢,查询区间的左端点为4,他左边的修改区间的右端点数为1,就是第一次修改。而查询区间的右端点为5,他右边的修改区间的左端点数为0;

        所以答案是4-1-0,为3;

(PS:表达能力有限,看代码或许容易理解些)


【代码】

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<ctime>
#include<iostream>
#include<algorithm>
#define _lowbit(x) (x&(-x))
using namespace std;
int N,M,tot=0;
int cl[50005],cr[50005];
void _in(int &x)
{
	char t=getchar();
	while(t<'0'||'9'<t) t=getchar();
	for(x=t-'0',t=getchar();'0'<=t&&t<='9';x=x*10+t-'0',t=getchar());
}
void _insert(int l,int r)     
{
	for(int i=l;i<=N;i+=_lowbit(i))   //cl记录左端点个数的前缀和 
	    cl[i]++;
	for(int i=r;i<=N;i+=_lowbit(i))   //cr记录右端点个数的前缀和 
	    cr[i]++;
}
int _findl(int l)  //找查询区间左边的右端点数 
{
	int ans=0;
	for(int i=l-1;i;i-=_lowbit(i))
	    ans+=cr[i];
	return ans;
}
int _findr(int r)       //找查询区间右边的左端点数 
{
	int ans=0;
	for(int i=N;i;i-=_lowbit(i))
	    ans+=cl[i];
	for(int i=r;i;i-=_lowbit(i))
	    ans-=cl[i];
	return ans;
}
void _init()
{
	_in(N);_in(M);
}
void _solve()
{
	int p,l,r;
	for(int i=1;i<=M;i++)
	{
		_in(p);_in(l);_in(r);
		if(p==1)
		{
			tot++;
			_insert(l,r);
		}
		else
		{
			int left=_findl(l);
			int right=_findr(r);
			printf("%d\n",tot-left-right);
		}
	}
}
int main()
{
	_init();
	_solve();
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值