贪婪大陆
描述 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<bits/stdc++.h>
#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;
}