五月集训(第24天)—线段树

线段树

破防了,线段树好难,啊不对,是线段树和扫描线放一起就有些不理解了,边抄边理解,理解了个大概,以后再深入一下

线段树模板

先来个线段树模板

// 建树 
void build(int s, int t, int p) {
	if (s == t) {
		d[p] = a[s];
		return ;
	}
	int mid = s + ((t - s) >> 1);
	build(s, mid, p * 2);
	build(mid + 1, t, p * 2 + 1);
	d[p] = d[p * 2] + d[p * 2 + 1];
}

// 区间和 
int get_sum(int l, int r, int s, int t, int p) {	/* [l, r]为查询区间 */
	if (l <= s && t <= r) return d[p];
	int mid = s + ((t - s) >> 1);
	int sum = 0;
	if (l <= mid) sum += get_sum(l, r, s, mid, p * 2);
	if (r > mid) sum += get_sum(l, r, mid + 1, t, p * 2 + 1);
	return sum; 
}

// 区间增加一个值
void update(int l, int r, int s, int t, int p, int c) {	/* c是区间增量 */
	if (l <= s && t <= r) {	// 如果当前区间为增加区间的子区间,则直接对d[]更新,并打上懒人标价 
		/* 到达一个不需要在分的区间,更新d[] */
		d[p] += c * (t - s + 1);
		b[p] += c;
		return ;
	}
	
	int mid = s + ((t - s) >> 1);
	if (b[p] && s != t) {	/* 如果查询的区间有懒人标记,则将标记下放到子区间 */
		// 到达一个需要再分的区间时,当前区间的d[]已经更新过了,为了保持一致性,将其分出的子区间的d[]也更新了 
		d[p * 2] += b[p] * (mid - s + 1), d[p * 2 + 1] += b[p] * (t - mid);	// 注意区间长度的计算,mid包含在左半区间 
		b[p * 2] += b[p], b[p * 2 + 1] += b[p];
		b[p] = 0;
	}
	if (l <= mid)  update(l, r, s, mid, p * 2, c);
	if (r > mid) update(l, r, mid + 1, t, p * 2 + 1, c);
	d[p] = d[p * 2] + d[p * 2 + 1];	// 子区间更新完了,直接利用子区间更新当前区间 
} 

// 带懒人标记处理的区间和查询 
int get_sum_lazy(int l, int r, int s, int t, int p) {
	if (l <= s && t <= r) return d[p];
	int mid = s + ((t - s) >> 1);
	if (b[p]) {
		/* 每个区间自己的懒人标记已经更新过 d[] 了,即对于子区间的修改也只需要将当前区间的b[]加给子区间的d[] */ 
		d[p * 2] += b[p] * (mid - s + 1), dp[p * 2 + 1] += b[p] * (t - mid);
		b[p * 2] += b[p], b[p * 2 + 1] += b[p];
		b[p] = 0;
	}
	int sum = 0;
	if (l <= mid) sum += get_sum_lazy(l, r, s, mid, p * 2);
	if (r > mid) sum += get_sum_lazy(l ,r, mid + 1, t, p * 2 + 1);
	return sum;
} 

/** 区间修改为某一值 **/
void update_change(int l, int r, int s, int t, int p, int c) {	/* 为区间[l, r]内的值将被修改为c */
	if (l <= s && t <= r) {
		d[p] = c * (t - s + 1);
		b[p] = c;
		return ;
	}
	int mid = s + ((t - s) >> 1);
	if (v[p]) {
		d[p * 2] = d[p] * (mid - s + 1), d[p * 2 + 1] = d[p] * (t - mid);
		b[p * 2] = b[p], b[p * 2 + 1] = b[p];
		v[p * 2] = v[p * 2 + 1] = 1;	// 打上区间修改的懒人标记
		v[p] = 0; 
	}
	if (l <= mid) update_change(l, r, s, mid, p * 2, c);
	if (mid < r) update_change(l, r, mid + 1, t, p * 2 + 1, c);
	d[p] = d[p * 2] + d[p * 2 + 1];
}

// 区间修改为某一值后的区间和查询 
int get_sum_v(int l, int r, int s, int t, int p) {
	if (l <= s && t <= r) return d[p];
	int mid = s + ((t - s) >> 1);
	if (v[p]) {
		d[p * 2] = d[p] * (mid - s + 1), d[p * 2 + 1] = d[p] * (t -mid);
		b[p * 2] = b[p], b[p * 2 + 1] = b[p];
		v[p * 2] = v[p * 2 + 1] = 1;
		v[p] = 0;
	}
	int sum = 0;
	if (l <= mid) sum += get_sum_v(l, r, s, mid, p * 2);
	if (r > mid) sum += get_sum_v(l, r, mid + 1, t, p * 2 + 1);
	return sum;
}

1. 850. 矩形面积 II

    思路:
        离散化 + 扫描线 + 线段树

class Solution {
    /*** 1.定义垂直线段 ***/
    #define mod 1000000007
    struct VSeg {
        int x;  // 垂直线段的 x 坐标
        int y1, y2; // 垂直线段的两个 y 坐标
        int v;  // +1(入边) 或者 -1(出边)

        VSeg() {}
        VSeg(int _x, int _y1, int _y2, int _v): x(_x), y1(_y1), y2(_y2), v(_v) {} 
    };

    vector<VSeg> vs;
    vector<int> Y;  // 存储离散化结果

    /*** 2.定义离散化相关的函数 ***/

    // 通过下标找值
    int get_value(int idx) {
        return Y[idx];
    }

    // 通过值找离散化的下标
    int get_index(int val) {
        int l = 0, r = Y.size() - 1;
        while (l <= r) {
            int mid = l + ((r - l) >> 1);
            if (Y[mid] == val) return mid;
            else if (val > Y[mid]) l = mid + 1;
            else r = mid - 1;
        }
        return -1;
    }

    /*** 3.定义线段树结点,其中len代表当前结点管辖的区间内完全覆盖的线段的长度总和,cover是线段完全覆盖区间的次数 ***/

    struct segNode {
        segNode *left, *right;
        int len;
        int cover;

        segNode() { // 初始化
            left = right = NULL;
            len = 0;
            cover = 0;
        }
    };

    /*** 4.递归构建线段树 ***/
    segNode* segtree_build(int l, int r) {
        if (l >= r) return NULL;
        segNode *root = new segNode();
        if (l + 1 == r) return root;    /* 若l==r则为点(没有面积),所以最小的结点区间长为1 */
        int mid = l + ((r - l) >> 1);
        root->left = segtree_build(l, mid);
        root->right = segtree_build(mid, r);
        return root;
    }

    /*** 5.插入线段过程 ***/

    // 两个线段是否相交
    bool is_not_intersect(int l, int r, int s, int t) {
        return r <= s || t <= l;    // 不相交
    }

    // 返回前一个线段是否完全包含后一个线段
    bool is_contain(int l, int r, int s, int t){
        return l <= s && t <= r;
    }

    // 更新当前树的len
    void update_root(segNode *root, int l, int r) {
        if (root->cover > 0) root->len = get_value(r) - get_value(l);
        else {
            if (l + 1 == r) root->len = 0;
            else merge_from_son(root);
        }
    }

    // 根据子节点计算当前树的len
    void merge_from_son(segNode *root) {
        root->len = root->left->len + root->right->len;
    }

    // 线段树的插入操作
    void segtree_insert(segNode *root, int l, int r, int s, int t, int val) {
        if (is_not_intersect(l, r, s, t)) return ;
        if (is_contain(l, r, s, t)) {
            root->cover += val;
            update_root(root, s, t);
            return ;
        }

        int mid = s + ((t - s) >> 1);
        segtree_insert(root->left, l, r, s, mid, val);
        segtree_insert(root->right, l, r, mid, t, val);
        update_root(root, s, t);
    }

public:
    int rectangleArea(vector<vector<int>>& rectangles) {
        int i;
        vs.clear();
        Y.clear();

        //  step1:将所有矩形用出边和入边表示,也就是两条垂直于x轴的线段
        int rect_size = rectangles.size();
        for (i = 0; i < rect_size; ++i) {
            vs.push_back( VSeg(rectangles[i][0], rectangles[i][1], rectangles[i][3], +1) );
            vs.push_back( VSeg(rectangles[i][2], rectangles[i][1], rectangles[i][3], -1) );
            Y.push_back( rectangles[i][1] );
            Y.push_back( rectangles[i][3] );
        }

        // step2: 将线段按照 x 坐标排序
        sort(vs.begin(), vs.end(), [&](const VSeg &a, const VSeg &b) {return a.x < b.x;} );
        sort(Y.begin(), Y.end());

        // step3: Y坐标进行离散化
        Y.erase( unique(Y.begin(), Y.end()), Y.end() );

        // step4: m代表了线段树的最大区间为 [0, m-1],也可以认为是 [0, m]
        int m = Y.size() - 1;
        long long ans = 0;
        segNode *root = segtree_build(0, m);

        int vs_size = vs.size();
        for (i = 0; i < vs_size; ++i) {
            int l = vs[i].y1;
            int r = vs[i].y2;
            int val = vs[i].v;

            if (i) {
                ans += (long long)root->len * (vs[i].x - vs[i - 1].x);
                ans %= mod;
            }
            segtree_insert(root, get_index(l), get_index(r), 0, m, val);
        }
        return ans;
    }
};

posted @ 2022-06-01 15:52  番茄元  阅读(28)  评论(0)    收藏  举报