CDOJ--卿学姐与基本法(线段树+离散化+区间查询)

原题链接: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;
}


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值