LuoguP1276校门外的树(增强版)
题目链接
读题可以发现,我们要实现区间整体变成\(0\),和区间内非\(0\)的位置建上一颗树。那么这个时候我们看到有区间推平操作就可以想到神奇的珂朵莉树(\(ODT\)),那么这个时候我们就要考虑将树木和树苗区分开,我们可以假定树苗是\(1\),树木的值是\(2\)。那么在砍树的时候直接推平整个区间,但是在推平的过程中要用\(res\)记录下有多少颗树苗被砍掉了;在植树的时候,我们不可以采用推平的操作,因为所选中的区间可能会有位置原来种有树木,所以我们要循环这个区间,将这个区间\(val = 0\)的位置变成\(1\)。
i64 res;
struct Tree {
struct Node {
int l, r;
mutable i64 val;
Node(int l, int r = 0, i64 val = 0) : l(l), r(r), val(val) {}
bool operator <(const Node& lhs) const {
return this -> l < lhs.l;
}
};
std::set<Node> s;
std::set<Node>::iterator split(int pos) {
std::set<Node>::iterator it = s.lower_bound(Node(pos));
if (it -> l == pos && it != s.end()) return it;
-- it;
if (it -> r < pos) return s.end();
i64 l = it -> l, r = it -> r, val = it -> val;
s.erase(it);
s.insert(Node(l, pos - 1, val));
return s.insert(Node(pos, r, val)).first;
}
void assaign(int l, int r, i64 x) {
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; ++ it)
if (it -> val == 2)
res += it -> r - it -> l + 1;
s.erase(itl, itr);
s.insert(Node(l, r, x));
}
void plant(int l, int r) {
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; ++ it)
if (it -> val == 0)
it -> val += 2;
}
void add(int l, int r, i64 x) {
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; ++ it)
if (it -> val == 0)
it -> val += x;
}
i64 query(int l, int r) {
i64 ans = 0;
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; it ++ )
ans += (it -> val == 2 ? 1 : 0) * (it -> r - it -> l + 1);
return ans;
}
};
int main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, m;
std::cin >> n >> m;
Tree ODT;
ODT.s.insert(Tree::Node(0, n, 1));
for (int i = 0; i < m; i ++ ) {
int x, l, r;
std::cin >> x >> l >> r;
if (x == 0) ODT.assaign(l, r, x);
else {
ODT.plant(l, r);
}
}
std::cout << ODT.query(0, n) << "\n" << res << "\n";
return 0 ^ 0;
}

浙公网安备 33010602011771号