原题链接:http://www.acm.uestc.edu.cn/#/contest/show/95
思路:
看了题目自然想到使用线段树进行区间查询,但是无奈N过大(1e8),普通写法必然爆内存,所以就要进行离散化处理。在读入操作区间时,将所有点进行存储,排序去重后用map做一个映射,这样就可以转化为一个较小范围的区间了(例如N = 1e8,存在两个操作:1 10 20, 2 15 20; 经过处理后区间长度仅为3,远远小于原本区间长度)。
当然,仅仅用上述离散化是无法在线段树中进行区间操作的,例如我们使用刚才上面的例子,将原本长度为N的区间映射为长度为3的区间,映射关系为:(10, 1), (15, 2), (20, 3);其在线段树的查询操作中区间分别为(1, 1, 3), (2, 1, 2), (3, 3, 3), (4, 1, 1), (5, 2, 2),是不是觉得有些不对劲? 没错,因为我们映射的是原来区间的点,这样在离散化的划分中,相隔点间的长度就会被丢失掉,(上例中如果我们查询的区间的起点和终点在标记2和标记3内,那么我们会丢失长度为3的区间!) 这种情况的处理办法就是在两个间隔不为1的点间插入一个和他们相邻的点,使得每次分割时从此处分割,这样处理后的区间就可以使用线段树进行区间操作了!(如果只是点操作应该不用作此处理吧..)
代码如下(自己写的代码,感觉并不是很好,找时间查查别人的离散化处理好好学学):
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<string>
#include<map>
#include<set>
#include<stack>
#include<cmath>
#include<sstream>
#include<queue>
#include<cctype>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int INF = 2147483647;
const int maxn = 1e5 + 5;
const int maxs = 16;
const ll MOD = 2016515;
const double PI = atan(1.0) * 4;
map<int, int> q;
vector<int> c2, d2, id;
int N, Q, t, ql, qr;
int w[2*maxn], c[2*maxn], d[2*maxn], sumv[4*maxn], _sum;
void update(int o, int L, int R) {
// 若区间已被完全覆盖则无需更新
if(sumv[o] == w[R] - w[L] + 1) return;
int lc = o*2, rc = o*2+1;
if(ql <= L && qr >= R) {
sumv[o] = w[R] - w[L] + 1;
}
else {
int M = L + (R - L) / 2;
// 将分割点转移至一对相邻点间
if(R - L != 1 && w[M+1] - w[M] != 1) {
if(M + 1 != R) M++;
else M--;
}
if(ql <= M) update(lc, L, M);
if(qr > M) update(rc, M+1, R);
sumv[o] = max(sumv[o], sumv[lc] + sumv[rc]);
}
}
void query(int o, int L, int R) {
if(R < ql || L > qr) return;
if(ql <= L && qr >= R) {
_sum += sumv[o];
}
else {
if(sumv[o] == w[R] - w[L] + 1) _sum += w[min(qr, R)] - w[max(ql, L)] + 1;
else {
int M = L + (R - L) / 2;
// 将分割点转移至一对相邻点间
if(R - L != 1 && w[M+1] - w[M] != 1) {
if(M + 1 != R) M++;
else M--;
}
if(ql <= M) query(o * 2, L, M);
if(qr > M) query(o * 2 + 1, M+1, R);
}
}
}
int main() {
scanf("%d%d", &N, &Q);
int n = 0, cnt = 0;
memset(sumv, 0, sizeof(sumv));
for(int i=1;i<=Q;i++) {
scanf("%d", &t);
scanf("%d%d", &ql, &qr);
if(t == 1) {
w[++cnt] = ql;
w[++cnt] = qr;
c[n] = ql;
d[n] = qr;
n++;
}
else {
w[++cnt] = ql;
w[++cnt] = qr;
c2.push_back(ql);
d2.push_back(qr);
id.push_back(i);
}
}
sort(w+1, w+cnt+1);
// 去重
cnt = unique(w + 1, w + cnt + 1) - w - 1;
// 插入用于分割的点
for(int i=cnt;i>0;i--) if(w[i-1] + 1 != w[i]) w[++cnt] = w[i-1] + 1;
sort(w+1, w+cnt+1);
// 映射
for(int i=1;i<=cnt;i++) q[w[i]] = i;
int k1 = 0, k2 = 0;
for(int i=1;i<=Q;i++) {
if(id[k2] != i) {
if(k1 == n) continue;
ql = q[c[k1]]; qr = q[d[k1]];
update(1, 1, cnt);
k1++;
}
else {
_sum = 0;
ql = q[c2[k2]]; qr = q[d2[k2]];
query(1, 1, cnt);
printf("%d\n", d2[k2] - c2[k2] + 1 - _sum);
k2++;
}
if(k2 == id.size()) break;
}
return 0;
}