贪婪大陆(线段树)

贪婪大陆

描述 Description :

人类被蚂蚁们逼到了 Greed Island 上的一个海湾。现在,小 孟晗(大佬) 的后方是一望无际的大海, 前方是变异了的超 级蚂蚁。 小 孟晗 还有大好前程,他可不想命丧于此, 于是他派遣手下最后一批改造 SCV 布置地雷以阻挡蚂蚁们的进攻。
小 孟晗 最后一道防线是一条长度为 N 的战壕, 小 孟晗 拥有无数多种地雷,而 SCV 每次 可以在[ L , R ]区间埋放同一种不同于之前已经埋放的地雷。 由于情况已经十万火急,小 孟晗 在某些时候可能会询问你在[ L’ , R’] 区间内有多少种不同的地雷, 他希望你能尽快的给 予答复。

输入格式 Input Format :

第一行为两个整数 n 和 m;
n 表示防线长度, m 表示 SCV 布雷次数及小 孟晗 询问 的次数总和。
接下来有 m 行, 每行三个整数 Q,L , R; 若 Q=1 则表示 SCV 在[ L , R ]这段区间 布上一种地雷, 若 Q=2 则表示小 孟晗 询问当前[ L , R ]区间总共有多少种地雷。
输出格式 Output Format
对于小 孟晗 的每次询问,输出一个答案(单独一行),表示当前区间地雷总数。

样例输入 Sample Input :

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

样例输出 Sample Output :

1
2

时间限制 Time Limitation :
1s
注释 Hint
对于 30%的数据: 0<=n, m<=1000;
对于 100%的数据: 0<=n, m<=10^5.

这道题乍一看以为是一道普通的线段树模板题,区间修改,区间查询。
但仔细一想就会发现不对劲,这道题询问的是雷的种类数。

这时候我们想一下,对于他查询的区间[L,R]
在这里插入图片描述
如果之前放置的区间的Ri在查询区间L的左边
那么这种地雷就肯定不在区间[L,R]内,
如果之前放置的区间的Li在查询区间R的左边
那么这种地雷就可能在区间[L,R]内,如果不在[L,R]内,不在的个数,和Ri在查询区间L左边的个数是相同的。

那么每次查询的种数 = 区间R左边Li的个数 - 查询区间L左边Ri的个数

显然我们用一颗线段树去维护似乎做不到,
所以我们要用两颗,一颗存左端点左边Ri的数,一颗存右端点左边Li的数。

emmm… 普通的递归线段树似乎有点长

所以我们可以用更简洁的非递归版的线段树,这样写起来就很简单了。
不会非递归版的线段树的童鞋可以点这里

#include

#define ll long long
const ll MAXN = 1 << 18 - 1;

int n,m,p,l,r;
int t1[1 << 18], t2[1 << 18];

int main()
{
	scanf("%d%d",&n,&m);
	while(m--){
		scanf("%d%d%d",&p,&l,&r);
		if(p == 1){//反向修改 
			for(int i = MAXN + l; i ; i >>= 1) ++ t1[i];
			for(int i = MAXN + r ; i ; i >>= 1) ++ t2[i];
		}
		else {
			int le = MAXN;
			l += MAXN;//最深的子节点处
			int sum1 = 0, sum2 = 0;//1为左端点右边r数 2为右端点左边l数 
			while(le ^ l ^ 1){
				if(~ le & 1) sum1 += t2[le ^ 1];
				if(l & 1) sum1 += t2[l ^ 1];
				le >>= 1;
				l >>= 1; 
			}
			le = MAXN, r = MAXN + r + 1;
			while(le ^ r ^ 1){
				if(~le & 1) sum2 += t1[le ^ 1];
				if(r & 1) sum2 += t1[r ^ 1];
				le >>= 1;
				r >>= 1;
			}
			printf("%d\n", sum2 - sum1);
		}
	}
	return 0;
}

你可能感兴趣的:(线段树)