F. MEX Queries - 线段树 + 离散化
作者:互联网
F. MEX Queries
题意
1 l r 将区间l r 置 1
2 l r 将区间l r 置 0
3 l r 翻转区间l r(即 是1置0 是0置1)
求每次操作后值是0的最左位置编号
思路
用线段树维护 区间和
用一个lazy 标记当前结点 置1 置0 翻转 或者 无需操作(用于减少时间复杂度)
对于翻转操作 每次更新一个结点的lazy要判断之前lazy是什么 然后根据之前那个lazy和新赋的lazy给出新的lazy
置零和置一操作直接更新lazy即可
更新区间和:
置零:tree[i].sum = 0;
置一:tree[i].sum = tree[i].r - tree[i].l + 1;(区间长即区间和)
翻转:tree[i].sum = tree[i].r - tree[i].l + 1 - tree[i].sum;(区间长减去原来的区间和即新的区间和)
最后询问整个区间 query时要下放lazy(要访问就必须下放lazy的信息 为访问到就暂时不用下放信息)
离散化:
因为给的数据l r会很大可达1e18(肯定tle)而操作次数只有1e5故要离散化
每次的答案只可能是给出的l 或 r+1 或 1 所以只要把l, r+1和 1放入用于离散化的数组 因为r时区间边界要用于查询更新等操作所以也要放入数组中
然后数组排序后离散化去重 将l r在离散化数组中的位置区间当作对应区间操作即可
#include<bits/stdc++.h>
#include<unordered_map>
#define ll long long
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-4;
const ll N = 1e18 + 5;
const int M = 1e5 + 5;
const int mod = 1e9 + 7;
ll n, op[M], l[M], r[M], a[M * 3], b[M * 3];//一定要开三倍大小 因为每次都要放进去 l r r+1
map<ll, ll>mp;
struct node {
ll l, r, sum, lazy;
}tree[M * 12];//一般线段树要开4倍 这里要再乘以3(l r r+1) 所以是12倍
void push_up(ll p) {
tree[p].sum = tree[p << 1].sum + tree[p << 1|1].sum;
}
//下方操作
void push_down(ll p) {
if (tree[p].lazy == -1) return;
if (tree[p].lazy == 1) {//如果父节点lazy是置1 左右儿子的lazy直接是置1
tree[p << 1].lazy = tree[p].lazy;
tree[p << 1 | 1].lazy = tree[p].lazy;
tree[p << 1].sum = tree[p << 1].r - tree[p << 1].l + 1;
tree[p << 1 | 1].sum = tree[p << 1 | 1].r - tree[p << 1 | 1].l + 1;
}
else if (tree[p].lazy == 0) {//如果父节点lazy是置0 左右儿子的lazy直接是置0
tree[p << 1].lazy = tree[p].lazy;
tree[p << 1 | 1].lazy = tree[p].lazy;
tree[p << 1].sum = tree[p << 1 | 1].sum = 0;
}
else {//如果父节点lazy是翻转 要判断儿子结点之前的lazy标记再相应翻转
//每次都先把把儿子结点的sum值更新一下 不然会出现多个lazy值重叠而sum值却迟迟没更新
tree[p << 1].sum = tree[p << 1].r - tree[p << 1].l + 1 - tree[p << 1].sum;
tree[p << 1|1].sum = tree[p << 1|1].r - tree[p << 1|1].l + 1 - tree[p << 1|1].sum;
ll x = tree[p << 1].lazy;
if (x == 0) tree[p << 1].lazy = 1;//之前lazy是0 就标记成1
else if (x == 1) tree[p << 1].lazy = 0;//之前lazy是1 就标记成0
else if (x == 3) tree[p << 1].lazy = -1;//之前lazy是翻转 就不用变
else tree[p << 1].lazy = 3;//之前lazy是未标记 就标记成翻转
x = tree[p << 1 | 1].lazy;
if (x == 0) tree[p << 1 | 1].lazy = 1;
else if (x == 1) tree[p << 1 | 1].lazy = 0;
else if (x == 3) tree[p << 1 | 1].lazy = -1;
else tree[p << 1 | 1].lazy = 3;
}
tree[p].lazy = -1;//下放父亲节点后 lazy置为未标记
}
void build(ll l, ll r, ll p) {
tree[p].l = l; tree[p].r = r;
tree[p].lazy = -1;//放在if外面 每个结点的lazy都要初始化为-1
if (l == r) {
tree[p].sum = 0;
return;
}
ll mid = (l + r) / 2;
build(l, mid, p << 1);
build(mid + 1, r, p << 1 | 1);
push_up(p);
}
void update1(ll xl, ll xr, ll p) {
ll l = tree[p].l, r = tree[p].r;
if (l >= xl && r <= xr) {
tree[p].sum = r - l + 1;
tree[p].lazy = 1;
return;
}
push_down(p);//更新完一个结点就下放一下信息
ll mid = (l + r) / 2;
if (mid >= xl) update1(xl, xr, p << 1);
if (mid < xr) update1(xl, xr, p << 1 | 1);
push_up(p);
}
void update2(ll xl, ll xr, ll p) {
ll l = tree[p].l, r = tree[p].r;
if (l >= xl && r <= xr) {
tree[p].sum = 0;
tree[p].lazy = 0;
return;
}
push_down(p);
ll mid = (l + r) / 2;
if (mid >= xl) update2(xl, xr, p << 1);
if (mid < xr) update2(xl, xr, p << 1|1);
push_up(p);
}
void update3(ll xl, ll xr, ll p) {
ll l = tree[p].l, r = tree[p].r;
if (l >= xl && r <= xr) {
//先更新sum避免后面没更新
tree[p].sum = tree[p].r - tree[p].l + 1 - tree[p].sum;
//对于lazy更新要结合之前的lazy标记
if (tree[p].lazy == 0) tree[p].lazy = 1;
else if (tree[p].lazy == 1) tree[p].lazy = 0;
else if (tree[p].lazy == -1) tree[p].lazy = 3;
else tree[p].lazy = -1;
return;
}
push_down(p);
ll mid = (l + r) / 2;
if (mid >= xl) update3(xl, xr, p << 1);
if (mid < xr) update3(xl, xr, p << 1|1);
push_up(p);
}
ll query(ll p) {
ll l = tree[p].l, r = tree[p].r;
if (l == r) return b[l];
push_down(p);
if (tree[p << 1].sum < (l + r) / 2 - l + 1) return query(p << 1);
else return query(p << 1|1);
}
void solve()
{
cin >> n;
ll tot = 0;
for (int i = 1; i <= n; i++) {
cin >> op[i] >> l[i] >> r[i];
b[++tot] = l[i];
b[++tot] = r[i];
b[++tot] = r[i] + 1;
}
b[++tot] = 1;
sort(b + 1, b + 1 + tot);//排序
ll len = unique(b + 1, b + (ll)1 + tot) - b - 1;//离散化去重
for (int i = 1; i <= len; i++) //离散化对应
mp[b[i]] = i;
build(1, len, 1);
for (int i = 1; i <= n; i++) {
if (op[i] == 1) update1(mp[l[i]], mp[r[i]], 1);
else if (op[i] == 2) update2(mp[l[i]], mp[r[i]], 1);
else update3(mp[l[i]], mp[r[i]], 1);
cout << query(1) << "\n";
}
}
signed main()
{
IOS;
int t = 1;
//cin >> t;
while (t--)
{
solve();
}
}
标签:lazy,xl,线段,tree,tot,MEX,Queries,区间,ll 来源: https://www.cnblogs.com/yaqu-qxyq/p/16218075.html