P2572 序列操作

题目链接

  需要实现区间覆盖区间01取反区间求和以及区间查询最大连续段。区间覆盖很好实现,区间\(01\)取反只需要用分别统计\(01\)个数的时候将他俩交换就可以了,区间求和在取反之后只需要\(len - sum\)就可以求出来了。重点就是区间最大连续子段,分成三类:
    \(1.\)左区间全满且跨越到右区间
    \(2.\)右区间全满且跨越到左区间
    \(3.\)横跨左右区间但是都未满
  在标记上传的时候需要修改的信息比较多

	Info operator + (const Info& a, const Info& b) {
	    Info c;
	    c.siz = a.siz + b.siz;
	    c.sum = a.sum + b.sum;

	    for (int i = 0; i < 2; i++) {
	        c.pre[i] = a.pre[i];
	        if (i == 1 && a.sum == a.siz) {
	            c.pre[i] += b.pre[i];
	        } else if (i == 0 && a.sum == 0) {
	            c.pre[i] += b.pre[i];
	        }

	        c.suf[i] = b.suf[i];
	        if (i == 1 && b.sum == b.siz) {
	            c.suf[i] += a.suf[i];
	        } else if (i == 0 && b.sum == 0) {
	            c.suf[i] += a.suf[i];
	        }

	        c.val[i] = b.pre[i] + a.suf[i];
	        c.val[i] = std::max(c.val[i], a.val[i]);
	        c.val[i] = std::max(c.val[i], b.val[i]);
	        c.val[i] = std::max(std::max(c.val[i], c.pre[i]), c.suf[i]);
	    }

	    return c;
	}

  标记下传的时候注意先后顺序,应该是先执行区间覆盖操作,再执行区间反转操作,而且在区间覆盖操作完之后应该是要吧原有的区间翻转的标记清空的。

	void settag1(int u, int x) {
	    tr[u].res.sum = x * tr[u].res.siz;
	    tr[u].tag.tag1 = x;
	    tr[u].tag.tag2 = 0;
	    tr[u].res.val[x] = tr[u].res.pre[x] = tr[u].res.suf[x] = tr[u].res.siz;
	    tr[u].res.pre[x ^ 1] = tr[u].res.suf[x ^ 1] = tr[u].res.val[x ^ 1] = 0;
	}

	void settag2(int u) {
	    tr[u].res.sum = tr[u].res.siz - tr[u].res.sum;
	    if (~tr[u].tag.tag1) {
	        tr[u].tag.tag1 ^= 1;
	    } else {
	        tr[u].tag.tag2 ^= 1;
	    }

	    std::swap(tr[u].res.val[0], tr[u].res.val[1]);
	    std::swap(tr[u].res.pre[0], tr[u].res.pre[1]);
	    std::swap(tr[u].res.suf[0], tr[u].res.suf[1]);
	}

	void push(int u) {
	    if (~tr[u].tag.tag1) {
	        tr[u].tag.tag2 = 0;
	        settag1(u << 1, tr[u].tag.tag1);
	        settag1(u << 1 | 1, tr[u].tag.tag1);
	        tr[u].tag.tag1 = -1;
	    }
	    if (tr[u].tag.tag2) {
	        settag2(u << 1);
	        settag2(u << 1 | 1);
	        tr[u].tag.tag2 = 0;
	    }
	}

  剩下的就是中规中矩的线段树操作了

	#include <bits/stdc++.h>

	using i64 = long long;

	#define rep(i,a,n) for (int i = a; i < n; i ++ )
	#define per(i,a,n) for (int i = n; i >= a; i -- )
	#define all(v) v.begin(), v.end()
	#define SZ(s) int(s.size())
	#define pb push_back
	#define fi first
	#define se second
	//head

	const int N = 100010;

	int a[N];

	struct Info {
	    int siz, sum;
	    int pre[2], suf[2], val[2];
	};

	struct Tag {
	    int tag1, tag2;
	    Tag() : tag1(-1), tag2(0) {}
	};

	Info operator + (const Info& a, const Info& b) {
	    Info c;
	    c.siz = a.siz + b.siz;
	    c.sum = a.sum + b.sum;

	    for (int i = 0; i < 2; i++) {
	        c.pre[i] = a.pre[i];
	        if (i == 1 && a.sum == a.siz) {
	            c.pre[i] += b.pre[i];
	        } else if (i == 0 && a.sum == 0) {
	            c.pre[i] += b.pre[i];
	        }

	        c.suf[i] = b.suf[i];
	        if (i == 1 && b.sum == b.siz) {
	            c.suf[i] += a.suf[i];
	        } else if (i == 0 && b.sum == 0) {
	            c.suf[i] += a.suf[i];
	        }

	        c.val[i] = b.pre[i] + a.suf[i];
	        c.val[i] = std::max(c.val[i], a.val[i]);
	        c.val[i] = std::max(c.val[i], b.val[i]);
	        c.val[i] = std::max(std::max(c.val[i], c.pre[i]), c.suf[i]);
	    }

	    return c;
	}

	struct Node {
	    Info res;
	    Tag tag;
	} tr[N << 2];

	void build(int u, int l, int r) {
	    if (l == r) {
	        tr[u].res.pre[a[l]] = tr[u].res.suf[a[l]] = tr[u].res.val[a[l]] = tr[u].res.siz = 1;
	        tr[u].res.sum = a[l];
	        return ;
	    }
	    int mid = (l + r) >> 1;
	    build(u << 1, l, mid);
	    build(u << 1 | 1, mid + 1, r);
	    tr[u].res = tr[u << 1].res + tr[u << 1 | 1].res;
	}

	void settag1(int u, int x) {
	    tr[u].res.sum = x * tr[u].res.siz;
	    tr[u].tag.tag1 = x;
	    tr[u].tag.tag2 = 0;
	    tr[u].res.val[x] = tr[u].res.pre[x] = tr[u].res.suf[x] = tr[u].res.siz;
	    tr[u].res.pre[x ^ 1] = tr[u].res.suf[x ^ 1] = tr[u].res.val[x ^ 1] = 0;
	}

	void settag2(int u) {
	    tr[u].res.sum = tr[u].res.siz - tr[u].res.sum;
	    if (~tr[u].tag.tag1) {
	        tr[u].tag.tag1 ^= 1;
	    } else {
	        tr[u].tag.tag2 ^= 1;
	    }

	    std::swap(tr[u].res.val[0], tr[u].res.val[1]);
	    std::swap(tr[u].res.pre[0], tr[u].res.pre[1]);
	    std::swap(tr[u].res.suf[0], tr[u].res.suf[1]);
	}

	void push(int u) {
	    if (~tr[u].tag.tag1) {
	        tr[u].tag.tag2 = 0;
	        settag1(u << 1, tr[u].tag.tag1);
	        settag1(u << 1 | 1, tr[u].tag.tag1);
	        tr[u].tag.tag1 = -1;
	    }
	    if (tr[u].tag.tag2) {
	        settag2(u << 1);
	        settag2(u << 1 | 1);
	        tr[u].tag.tag2 = 0;
	    }
	}

	// assaign
	void change(int u, int l, int r, int ln, int rn, int x) {
	    push(u);
	    if (l >= ln && r <= rn) return void(settag1(u, x));
	    int mid = (l + r) >> 1;

	    if (mid >= ln) change(u << 1, l, mid, ln, rn, x);
	    if (mid < rn) change(u << 1 | 1, mid + 1, r, ln, rn, x);
	    tr[u].res = tr[u << 1].res + tr[u << 1 | 1].res;
	}

	void reverse(int u, int l, int r, int ln, int rn) {
	    push(u);
	    if (l >= ln && r <= rn) return void(settag2(u));
	    int mid = (l + r) >> 1;
	    if (mid >= ln) reverse(u << 1, l, mid, ln, rn);
	    if (mid < rn) reverse(u << 1 | 1, mid + 1, r, ln, rn);
	    tr[u].res = tr[u << 1].res + tr[u << 1 | 1].res;
	}

	Info query(int u, int l, int r, int ln, int rn) {
	    if (l >= ln && r <= rn) return tr[u].res;
	    int mid = (l + r) >> 1;
	    push(u);
	    if (mid >= rn) return query(u << 1, l, mid, ln, rn);
	    else if (mid < ln) return query(u << 1 | 1, mid + 1, r, ln, rn);
	    else return query(u << 1, l, mid, ln, rn) + query(u << 1 | 1, mid + 1, r, ln, rn);
	}

	signed main() {
	    std::cin.tie(nullptr)->sync_with_stdio(false);

	    int n, m;
	    std::cin >> n >> m;
	    for (int i = 1; i <= n; i++) 
	        std::cin >> a[i];

	    build(1, 1, n);

	    for (int i = 1; i <= m; i ++) {
	        int op, l, r;
	        std::cin >> op >> l >> r;
	        l++, r++;
	        if (op <= 1) {
	            change(1, 1, n, l, r, op);
	        } else if (op == 2) {
	            reverse(1, 1, n, l, r);
	        } else if (op == 3) {
	            std::cout << query(1, 1, n, l, r).sum << "\n";
	        } else {
	            std::cout << query(1, 1, n, l, r).val[1] << "\n";
	        }
	    }

	    return 0 ^ 0;
	}
posted @ 2022-08-31 19:04  浅渊  阅读(49)  评论(0)    收藏  举报