一道edu的线段树模板题
链接:https://ac.nowcoder.com/acm/contest/29320/M
来源:牛客网
HT 巨巨有个 nnn 个块(物理块)的磁盘,每个块只有被占用、空闲两种状态。我们赋予 nnn 个块 111 到 nnn 的编号,初始所有块都是空闲的。
对这个磁盘有两种操作:
- `1 x`,表示把第 xxx 个块的状态翻转,即如果当前被占用则释放,如果是空闲的则占用。
- `2 x`,表示询问占用 xxx 个块的文件最早可以安排在哪里。即找到编号最小的连续 xxx 块空闲,输出其左右端点的编号。如果不存在,输出 −1-1−1。
输入描述:
第一行有两个正整数 n,q,其中 1⩽n,q⩽1e5。
接下来 q 行,每行有两个正整数 op,x,表示询问,其中 op∈{1,2},1⩽x⩽n。
输出描述:
对于每个操作 2,分别用一行输出答案。如果答案存在,那么输出两个正整数 l,r表示这个区间的左右端点编号;如果不存在,输出 −1。
#include<bits/stdc++.h> #define ll long long #define FF ios::sync_with_stdio(false); cin.tie(0), cout.tie(0); using namespace std; const int inf = 0x3f3f3f3f; const int N = 1e5 + 10; int n, m, op, x, ans, a[N]; struct node{ //分别代表左右边界, 前缀最大空闲串 后缀最大空闲串 整个的最大空闲串 int l, r, prem, lastm, ans; }tree[N << 2];//4倍大小 //向上更新 void push_up(int num){ tree[num].l = tree[num << 1].l; tree[num].r = tree[num << 1|1].r; tree[num].ans =max(tree[num << 1].ans, tree[num << 1|1].ans); if(tree[num << 1].prem == tree[num << 1].r - tree[num << 1].l + 1){ tree[num].prem = tree[num << 1].prem + tree[num << 1|1].prem; } else tree[num].prem = tree[num << 1].prem; if(tree[num << 1|1].lastm == tree[num << 1|1].r - tree[num << 1|1].l + 1){ tree[num].lastm = tree[num << 1].lastm + tree[num << 1|1].lastm; } else tree[num].lastm = tree[num << 1|1].lastm; tree[num].ans = max(tree[num].ans, tree[num].prem); tree[num].ans = max(tree[num].ans, tree[num].lastm); tree[num].ans = max(tree[num].ans, tree[num << 1].lastm + tree[num <<1|1].prem); } //建树 void build(int l, int r,int num){ tree[num].l = l, tree[num].r = r; if(l == r){ //一开始都是空闲的 tree[num].ans = tree[num].prem = tree[num].lastm = 1; return; } build(l, (l + r) / 2, num << 1); build((l + r) / 2 + 1, r, num << 1|1); push_up(num); return; } //单点修改 void update(int pos, int num){ int l = tree[num].l, r = tree[num].r; if(l == r){ //每次异或1 可实现0 1变换 tree[num].ans = tree[num].prem = tree[num].lastm = tree[num].ans ^ 1; return; } int mid = (l + r) / 2; if(pos <= mid) update(pos, num << 1); else update(pos, num << 1|1); push_up(num); } //一定要注意条件的先后循序 因为要找最左的 int query(int v, int num){ //更新到叶节点 肯定是答案了 if(tree[num].l == tree[num].r) return tree[num].l; //当前节点前缀大于询问值 答案l就是该点的左边界 if(tree[num].prem >= v) { return tree[num].l; } //左儿子中最长串大于询问值就去左儿子中找 else if(tree[num << 1].ans >= v) query(v, num << 1); //左儿子的后缀加上右儿子的前缀比询问直达就是左儿子前缀的第一个数 else if(tree[num << 1].lastm + tree[num << 1|1].prem >= v); //右儿子的最长串比询问直大 去右儿子中找 else if(tree[num << 1|1].ans >= v) query(v, num << 1|1); //其余不存在 返回-1 else return -1; } void solve() { cin >> n >> m; for(int i = 1; i <= n; i++){ a[i] = 1; } build(1, n, 1); while(m--){ int l, r; cin >> op >> x; if(op == 1) update(x, 1); else { l = query(x, 1); r = l + x - 1; if(l != -1) cout << l << " " << r << "\n"; else cout << -1 << "\n"; } } } int main(){ FF; int t = 1; while(t--){ solve(); } return 0; }