洛谷P2894 [USACO08FEB]酒店Hotel
标签
- 线段树
- 区间合并
- 多种懒惰标记
简明题意
- 维护一个01序列,支持三种操作
- 询问连续x长的区间的最小左端点。若不存在则输出0
- 将区间[L,R]全置为0
- 将区间[L,R]全置为0
思路
-
主要说说tag的处理。由于对区间有两种操作,一个是置0,一个是置1,自然应该想到两种tag的处理方法。参见这里,较为详细地说明了如何处理多种标记。这篇博客里我再解释一次:
- 首先定义好你的tag
- 当你定义好多个tag后,首要的任务是分析通过多个tag得到当前节点的真实值的方法
- 由上面分析出的方法,再去定义tag间的运算(tag间的运算,指添加tag时的法则,也就是change时如何打一个tag而不影响别的tag)
- 如何传标记
回到这里,
- 首先定义好tag,我们规定一个tag1表示当前节点应住房,tag2表示当前节点应退房
- 接下来应该分析通过多个tag得到当前节点的真实值的方法。当tag1=1,tag2=1,时如何处理当前节点的真实值?我们发现存在tag时新来的tag是可以直接覆盖的呢,因为永远是以最后一次操作为基准,所以直接覆盖。因此不可能同时存在两种标记,直接覆盖即可。
- 接下来考虑spread,由上面我们直接覆盖就好了
-
其他的区间合并的方法比较简单,就不详细说了
注意事项
- 一定要注意区间归并时的max的更新方法,它是由左右子树的max而来的,而不是左右子树的lmax或rmax来的!!!(
好吧这是我自己不小心犯的错) - 区间合并的题一定要弄清楚自己现在处理的是左子树还是右子树,别搞反了~ (
这又是我自己犯的错)
总结
- 无
AC代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 50000 + 10;
struct Node
{
int l, r;
int lmax, rmax, max;
int len;
int tag1, tag2;
};
Node tree[maxn * 4];
void spread(int o)
{
if (tree[o].tag1 == 1)
{
tree[o].lmax = tree[o].rmax = tree[o].max = 0;
if (tree[o].l != tree[o].r)
{
tree[o * 2].tag2 = tree[o * 2 + 1].tag2 = 0;
tree[o * 2].tag1 = tree[o * 2 + 1].tag1 = 1;
}
tree[o].tag1 = 0;
}
if (tree[o].tag2 == 1)
{
tree[o].lmax = tree[o].rmax = tree[o].max = tree[o].len;
if (tree[o].l != tree[o].r)
{
tree[o * 2].tag2 = tree[o * 2 + 1].tag2 = 1;
tree[o * 2].tag1 = tree[o * 2 + 1].tag1 = 0;
}
tree[o].tag2 = 0;
}
}
void update(int o)
{
if (tree[o].l != tree[o].r)
{
spread(o * 2), spread(o * 2 + 1);
tree[o].lmax = tree[o * 2].lmax;
if (tree[o * 2].lmax == tree[o * 2].len)
tree[o].lmax += tree[o * 2 + 1].lmax;
tree[o].rmax = tree[o * 2 + 1].rmax;
if (tree[o * 2 + 1].rmax == tree[o * 2 + 1].len)
tree[o].rmax += tree[o * 2].rmax;
tree[o].max = max(tree[o * 2].max, tree[o * 2 + 1].max);
tree[o].max = max(tree[o].max, tree[o * 2].rmax + tree[o * 2 + 1].lmax);
}
}
void build(int o, int l, int r)
{
tree[o].l = l, tree[o].r = r;
tree[o].len = tree[o].lmax = tree[o].rmax = tree[o].max = r - l + 1;
if (l == r)
return;
int mid = (l + r) / 2;
build(o * 2, l, mid);
build(o * 2 + 1, mid + 1, r);
}
int ask(int o, int x)
{
spread(o);
if (tree[o].max < x)
return 0;
if (tree[o].lmax >= x || tree[o].l == tree[o].r)
return tree[o].l;
if (tree[o * 2].max >= x)
return ask(o * 2, x);
else if (tree[o * 2].rmax + tree[o * 2 + 1].lmax >= x)
return tree[o * 2].r - tree[o * 2].rmax + 1;
else
return ask(o * 2 + 1, x);
}
void change(int o, int l, int r, int type)
{
spread(o);
if (tree[o].l == l && tree[o].r == r)
{
if (type == 1)
tree[o].tag2 = 0, tree[o].tag1 = 1;
else
tree[o].tag2 = 1, tree[o].tag1 = 0;
spread(o);
return;
}
int mid = (tree[o].l + tree[o].r) / 2;
if (r <= mid)
change(o * 2, l, r, type);
else if (l > mid)
change(o * 2 + 1, l, r, type);
else
change(o * 2, l, mid, type), change(o * 2 + 1, mid + 1, r, type);
update(o);
}
int n, m;
void solve()
{
scanf("%d%d", &n, &m);
build(1, 1, n);
while (m--)
{
int opt;
scanf("%d", &opt);
if (opt == 1)
{
int x;
scanf("%d", &x);
int pos;
printf("%d\n", pos = ask(1, x));
if (pos != 0)
change(1, pos, pos + x - 1, 1);
}
else if (opt == 2)
{
int l, r;
scanf("%d%d", &l, &r);
change(1, l, l + r - 1, 2);
}
}
}
int main()
{
//freopen("Testin.txt", "r", stdin);
//freopen("Testout.txt", "w", stdout);
solve();
return 0;
}
双倍经验
- 无