bzoj4592[SHOI2015]脑洞治疗仪
题目链接
解析
3.20 正解是线段树,但是今天没时间写了,先挖个坑qwq,暂时就写个\(ODT\)
upd 3.21 (没有咕的)线段树解法
操作0:区间赋值
操作1:查询区间和,区间赋值
先查询区间\([l0, r0]\)的和\(sum\),得到可用的脑组织数并将\([l0, r0]\)赋\(0\),然后在\([l1, r1]\)上二分找到位置\(p\),满足\([l1, p]\)的中\(0\)的个数等于\(sum\)(如果没有,说明可用脑组织多了,直接把\(p\)设为\(r1\)就行)然后把\([l1, p]\)赋\(1\)
操作2:查询最长连续子段
复杂度\(O(n \log^2 n)\)
ODT解法
这个题珂朵莉树能过……BZOJ上还跑得飞快……
上面是线段树,下面是珂朵莉树……
操作0:直接\(assign\)
操作1:查出\([l0, r0]\)中\(1\)的个数,然后\(assign\),对\([l1, r1]\)暴力跑,找够了就\(assign\),但是注意不要经常\(split\),这里的\(assign\)能用之前的迭代器就不要再\(split\)去找了,不然珂朵莉也救不了你qwq($TLE$10发的教训qwq)
操作2:暴力跑一遍区间内节点就好了,要有信仰
另外通过此题发现的\(ODT\)注意事项:每次重新\(split\)了右端点之后一定要再\(split\)左端点一次,不然左边的迭代器可能被删除(下面的82行,也就是倒数第6行,好像markdown不显示行号……),这就是luogu上有篇题解会\(RE\)的原因
代码
线段树
#include <iostream>
#include <cstdio>
#include <cstring>
#define MAXN 200005
typedef long long LL;
struct SegmentTree {
struct Node {
int maxlen, llen, rlen, cov, sum;
} node[MAXN << 2];
void build(int, int, int);
void push_up(int, int, int);
void push_down(int, int, int);
void cover(int, int, int, int, int, int);
int query1(int, int, int, int, int);
int query(int, int, int, int, int);
void fix(int, int, int, int);
Node & operator [](int x) { return node[x]; }
};
int N, M;
SegmentTree tr;
int main() {
scanf("%d%d", &N, &M);
tr.build(1, 1, N);
while (M--) {
int tp, l0, r0;
scanf("%d%d%d", &tp, &l0, &r0);
if (tp == 0) tr.cover(1, 1, N, l0, r0, 0);
else if (tp == 1) {
int l1, r1; scanf("%d%d", &l1, &r1);
tr.fix(l0, r0, l1, r1);
} else printf("%d\n", tr.query(1, 1, N, l0, r0));
}
return 0;
}
void SegmentTree::push_up(int id, int L, int R) {
int ls = id << 1, rs = id << 1 | 1, mid = (L + R) >> 1;
if (node[ls].maxlen == mid + 1 - L) node[id].llen = mid + 1 - L + node[rs].llen;
else node[id].llen = node[ls].llen;
if (node[rs].maxlen == R - mid) node[id].rlen = R - mid + node[ls].rlen;
else node[id].rlen = node[rs].rlen;
node[id].maxlen = std::max(std::max(node[ls].maxlen, node[rs].maxlen), node[ls].rlen + node[rs].llen);
node[id].sum = node[ls].sum + node[rs].sum;
}
void SegmentTree::push_down(int id, int L, int R) {
int ls = id << 1, rs = id << 1 | 1, mid = (L + R) >> 1;
if (~node[id].cov) {
node[ls].cov = node[rs].cov = node[id].cov;
node[ls].sum = node[id].cov * (mid + 1 - L);
node[ls].llen = node[ls].rlen = node[ls].maxlen = mid + 1 - L - node[ls].sum;
node[rs].sum = node[id].cov * (R - mid);
node[rs].llen = node[rs].rlen = node[rs].maxlen = R - mid - node[rs].sum;
node[id].cov = -1;
}
}
void SegmentTree::build(int id, int L, int R) {
node[id].cov = -1;
node[id].llen = node[id].rlen = node[id].maxlen = 0;
node[id].sum = R - L + 1;
if (L == R) return;
int mid = (L + R) >> 1;
build(id << 1, L, mid);
build(id << 1 | 1, mid + 1, R);
}
void SegmentTree::cover(int id, int L, int R, int l, int r, int v) {
if (L >= l && R <= r) {
node[id].cov = v;
node[id].sum = v * (R - L + 1);
node[id].maxlen = node[id].llen = node[id].rlen = R - L + 1 - node[id].sum;
} else {
push_down(id, L, R);
int mid = (L + R) >> 1;
if (l <= mid) cover(id << 1, L, mid, l, r, v);
if (r > mid) cover(id << 1 | 1, mid + 1, R, l, r, v);
push_up(id, L, R);
}
}
int SegmentTree::query1(int id, int L, int R, int l, int r) {
if (L >= l && R <= r) return node[id].sum;
push_down(id, L, R);
int mid = (L + R) >> 1, res = 0;
if (l <= mid) res += query1(id << 1, L, mid, l, r);
if (r > mid) res += query1(id << 1 | 1, mid + 1, R, l, r);
return res;
}
int SegmentTree::query(int id, int L, int R, int l, int r) {
if (L >= l && R <= r) return node[id].maxlen;
push_down(id, L, R);
int mid = (L + R) >> 1;
if (r <= mid) return query(id << 1, L, mid, l, r);
else if (l > mid) return query(id << 1 | 1, mid + 1, R, l, r);
else {
int res = std::max(query(id << 1, L, mid, l, mid), query(id << 1 | 1, mid + 1, R, mid + 1, r));
res = std::max(res, std::min(node[id << 1].rlen, mid - l + 1) + std::min(node[id << 1 | 1].llen, r - mid));
return res;
}
}
void SegmentTree::fix(int l0, int r0, int l1, int r1) {
int tot = query1(1, 1, N, l0, r0);
if (!tot) return;
cover(1, 1, N, l0, r0, 0);
int l = l1, r = r1;
while (l < r) {
int mid = (l + r) >> 1;
if (mid - l1 + 1 - query1(1, 1, N, l1, mid) < tot) l = mid + 1;
else r = mid;
}
cover(1, 1, N, l1, l, 1);
}
//Rhein_E
ODT
#include <cstdio>
#include <iostream>
#include <cstdio>
#include <set>
#include <algorithm>
typedef long long LL;
struct ODT_Node {
int l, r; char val;
ODT_Node (int _l, int _r, char _v):l(_l), r(_r), val(_v) {}
ODT_Node (int p = 0):l(p), r(p), val(0) {}
bool operator <(const ODT_Node &n) const { return l < n.l; }
};
typedef std::set<ODT_Node>::iterator iter;
struct ODT {
std::set<ODT_Node> data;
void init(int n = 0) { data.clear(); data.insert(ODT_Node(1, n, 1)); }
iter split(int);
void assign(int, int, char);
int query(int, int);
};
void fix(int, int, int, int);
ODT odt;
int N, M;
int main() {
scanf("%d%d", &N, &M);
odt.init(N);
while (M--) {
int tp, l0, r0;
scanf("%d%d%d", &tp, &l0, &r0);
if (tp == 0) odt.assign(l0, r0, 0);
else if (tp == 1) {
int l1, r1;
scanf("%d%d", &l1, &r1);
fix(l0, r0, l1, r1);
} else printf("%d\n", odt.query(l0, r0));
}
return 0;
}
inline iter ODT::split(int pos) {
iter it = data.lower_bound(ODT_Node(pos));
if (it->l == pos) return it;
--it;
int l = it->l, r = it->r; char v = it->val;
data.erase(it);
data.insert(ODT_Node(l, pos - 1, v));
return data.insert(ODT_Node(pos, r, v)).first;
}
inline void ODT::assign(int l, int r, char v) {
iter right = split(r + 1), left = split(l);
data.erase(left, right);
data.insert(ODT_Node(l, r, v));
}
inline int ODT::query(int l, int r) {
int res = 0, tmp = 0;
iter right = split(r + 1), left = split(l);
for (iter i = left; i != right; ++i)
if (i->val) res = std::max(res, tmp), tmp = 0;
else tmp += i->r - i->l + 1;
return std::max(res, tmp);
}
void fix(int l0, int r0, int l1, int r1) {
int tot = 0, blank = 0;
iter right = odt.split(r0 + 1), left = odt.split(l0), it;
for (it = left; it != right; ++it)
if (it->val) tot += it->r - it->l + 1;
odt.data.erase(left, right);
odt.data.insert(ODT_Node(l0, r0, 0));
if (!tot) return;
right = odt.split(r1 + 1), left = odt.split(l1);
for (it = left; it != right && blank < tot; ++it)
if (!it->val) blank += it->r - it->l + 1;
if (blank < tot) {
odt.data.erase(left, right);
odt.data.insert(ODT_Node(l1, r1, 1));
} else {
int rr = (--it)->r - blank + tot;
right = odt.split(rr + 1), left = odt.split(l1);
odt.data.erase(left, right);
odt.data.insert(ODT_Node(l1, rr, 1));
}
}
//Rhein_E