yuwj  

写在前面

终于打算好好整理笔记了,整理真的是提升水平的最关键要素之一,至少这个方法真的适合自己,为什么之前一直没有做?一个字,懒,两个字,太懒,三个字,执行力

好了好了

这一篇是线段树区间合并,

核心思想是:是否跨越分治中心,维护前后缀信息

经典场景:连续区间最大长度,线段树解决最大子段和

poj 3667

题意

初始0,两种操作,

查询:len 查找能够放置区间的最左端,并修改成1

修改:pos,len 从pos开始的长度为len的区间修改为0

思路

找最左边的符合的连续区间长度就好了

考虑线段树节点之间合并,即信息维护:

需要4个Info和1个tag标记

pre,suf,sum,len,分别表示区间前缀连续段长度,区间后缀连续段长度,区间内最大连续段长度,区间长度

tag,懒标记,-1表示空节点,0表示标记为空,1表示标记为入住(占用),使用注意:build就要初始化lazy标记,使用需要考虑标记合并于标记应用于节点,不要忘记标记下传

合并节点:分别考虑如何更新各个info

 区间合并的题目大部分都是这么合并的,详细见代码(也是简单的一点)

区间修改:lazy_tag的魔力,settag即可,然后记得modify和query都push_down

 settag:给节点打标记,修改在节点上执行,本题我将标记合并与应用标记都写在settag中

然后这个题目的最左端点的查询:重点关注查询顺序,先左区间,再看中缀,最后右区间,端点就是区间长度+端点就行了,到了叶子就直接是端点

点击查看代码
/*
线段树合并:
实际上是找一段最大连续区间,并且满足最左边即可
考虑最大连续区间:经典线段树合并可以解决最大连续区间长度, 核心是是否跨过分治中心
考虑最左端点:结论是考虑查找顺序,先考虑左边,再考虑跨过分治中心,最后考虑右边
如果查找的区间长度是包含在其中的就可以放了,然后返回左端点就行
端点查找具体位置:左前缀l,中点mid-lsuf,右后缀:r-rsuf

pre,suf,sum区间最大连续段长度
*/
#include <iostream>
#include <algorithm>
using namespace std;
const int MAXN=2e5+10;
struct Info{
	int pre,suf,sum,len;
	Info(){}
	Info(int x,int l):pre(x),suf(x),sum(x),len(l){}
};
Info operator+(const Info &l,const Info &r){
	Info res;
	res.len = l.len + r.len,res.pre = l.pre,res.suf=r.suf;
	if(l.len == l.pre) res.pre = l.pre+r.pre;
	if(r.len == r.suf) res.suf = r.suf+l.suf;
	res.sum = max(l.sum,r.sum); 
	res.sum = max(res.sum, l.suf + r.pre);
	return res;
}
struct SegTree{
	Info s;
}seg[MAXN*4];
struct Tag{
	int t; 
}tag[MAXN*4];
void settag(int idx,int op){
	tag[idx].t=op;
	if(op == 0) seg[idx].s.sum = seg[idx].s.pre = seg[idx].s.suf = seg[idx].s.len;
	else seg[idx].s.sum = seg[idx].s.suf = seg[idx].s.pre = 0;
}
void push_down(int idx){
	if(tag[idx].t!=-1){
		settag(idx*2,tag[idx].t);
		settag(idx*2+1,tag[idx].t);
		tag[idx].t=-1;
	}
}
void build(int l,int r,int idx){
	tag[idx].t = -1;
	if(l == r){seg[idx].s=Info(1,1);return;}
	int mid=(r+l)>>1;
	build(l,mid,idx*2);
	build(mid+1,r,idx*2+1);
	seg[idx].s=seg[idx*2].s+seg[idx*2+1].s;
}

void modify(int l,int r,int idx,int ql,int qr,int op){
	if(ql<=l && r <= qr){settag(idx,op);return;}
	push_down(idx);
	int mid = (r+l) >> 1;
	if(ql<=mid) modify(l,mid,idx*2,ql,qr,op);
	if(qr > mid) modify(mid+1,r,idx*2+1,ql,qr,op);
	seg[idx].s=seg[idx*2].s + seg[idx*2+1].s;
}

int query(int l,int r,int idx,int len){
	push_down(idx);
	if(seg[idx].s.sum < len) return 0;
	if(l == r) return l;
	int mid = (l+r) >> 1;
	if(seg[idx*2].s.sum >= len) return query(l,mid,idx*2,len);
	else if(seg[idx*2].s.suf + seg[idx*2+1].s.pre >= len) return mid - seg[idx*2].s.suf + 1;
	else return query(mid+1,r,idx*2+1,len);
}
int main(){
	ios::sync_with_stdio(0);cin.tie(0);
	int n,q;cin >> n >> q;
	build(1,n,1);
	while(q--){
		int op, len,pos; cin >> op;
		if(op == 1) {
			cin>>len; pos = query(1,n,1,len);cout<<pos<<'\n';
			if(pos>0)modify(1,n,1,pos,pos+len-1,1);
		}
		else {
			cin>>pos>>len; modify(1,n,1,pos,pos+len-1,0);
		}
	}
	return 0;
}

Hdu 1540

这个题目是之前写得,有点忘记了,这里就放之前的记录了,总之又是一个调了一下午的题目,最后实在没辙上了AI,可恶

题意

初始数组全为1,三种操作:

操作1,位置变0

操作2,查询位置所处的最长连续1段

操作3,0恢复1

思路

线段树合并

线段树,从位置中点开始往左右两边查询前后缀就是答案

点击查看代码
/*
线段树合并关键点总结:
1.相邻区间合并到一起
2.考虑跨过分治点,即前缀和后缀,更新就是取max()
想一想线段是怎么合并的?
ans=左区间+右区间,左后缀+右前缀
区间后缀和前缀是怎么计算的?
区间后缀 =  左后缀+右区间len,右后缀()
查询是找左区间后缀+右区间前缀

卡点:
爆内存调了1h看不懂是为什么
build和change没有return
错了是为什么?
1.最近的一次摧毁记录错误 -> 栈/数组记录,我用了个变量记录,大错特错
2.信息更新错误 -> 父亲接受儿子的信息错误,这个题目是连续1的数量,我还是按照子段和的逻辑更新信息,但是这里是有条件的,子段和可以不接都行,但是这里不一样,必须能接上才能更新
3.信息查询错误,完全不懂怎么查询 -> 就跟单点修改一样定位查询就行,怎么精准定位?
有说法的:查看pos是否包含在某一段连续区间内,如果在就直接返回这段区间(合并的左+右)就行了,否则就说明还没在一块连通块内,那就再靠近一点点!
直到碰到了叶子结点
4.忘了建树
*/
#include <cstdio>
#include <stack>
using namespace std;
const int MAXN = 50010;

struct Info {
    int mpre, msuf, len;
    Info() : mpre(0), msuf(0), len(0) {}
    Info(int v, int l) : len(l) {
        mpre = msuf = v;
    }
};

Info operator+ (const Info& l, const Info& r) {
    Info res;
    res.len = l.len + r.len;
    res.mpre = l.mpre;
    if (l.mpre == l.len) {
        res.mpre = l.len + r.mpre;
    }
    res.msuf = r.msuf;
    if (r.msuf == r.len) {
        res.msuf = r.len + l.msuf;
    }
    return res;
}

struct SegTree {
    Info s;
} seg[MAXN * 4];

void build(int l, int r, int idx) {
    if (l == r) {
        seg[idx].s = Info(1, 1);
        return;
    }
    int mid = (l + r) >> 1;
    build(l, mid, idx * 2);
    build(mid + 1, r, idx * 2 + 1);
    seg[idx].s = seg[idx * 2].s + seg[idx * 2 + 1].s;
}

void change(int l, int r, int idx, int pos, int val) {
    if (l == r) {
        seg[idx].s = Info(val, 1);
        return;
    }
    int mid = (l + r) >> 1;
    if (pos <= mid) {
        change(l, mid, idx * 2, pos, val);
    } else {
        change(mid + 1, r, idx * 2 + 1, pos, val);
    }
    seg[idx].s = seg[idx * 2].s + seg[idx * 2 + 1].s;
}

Info query(int l, int r, int idx, int ql, int qr) {
    if (ql <= l && r <= qr) {
        return seg[idx].s;
    }
    int mid = (l + r) >> 1;
    if (qr <= mid) {
        return query(l, mid, idx * 2, ql, qr);
    }
    if (ql > mid) {
        return query(mid + 1, r, idx * 2 + 1, ql, qr);
    }
    Info left_info = query(l, mid, idx * 2, ql, mid);
    Info right_info = query(mid + 1, r, idx * 2 + 1, mid + 1, qr);
    return left_info + right_info;
}

int main() {
    int n, m;
    while (scanf("%d%d", &n, &m) == 2) {
        build(1, n, 1);
        stack<int> stk;
        while (m--) {
            char op[2];
            scanf("%s", op);
            if (op[0] == 'D') {
                int pos;
                scanf("%d", &pos);
                change(1, n, 1, pos, 0);
                stk.push(pos);
            } else if (op[0] == 'R') {
                if (!stk.empty()) {
                    int pos = stk.top();
                    stk.pop();
                    change(1, n, 1, pos, 1);
                }
            } else if (op[0] == 'Q') {
                int pos;
                scanf("%d", &pos);
                int left_len = query(1, n, 1, 1, pos).msuf;
                int right_len = query(1, n, 1, pos, n).mpre;
                int ans = left_len + right_len-1;
                if (ans < 0) ans = 0;
                printf("%d\n", ans);
            }
        }
    }
    return 0;
}

然后我就来挑战一下09年多校,被打爆了

Hdu 2871

真的做不动一点。。。到此为止了。。。

posted on 2025-06-25 13:27  xiaowang524  阅读(56)  评论(0)    收藏  举报