一道edu的線段樹模板題
阿新 • • 發佈:2022-03-21
連結: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; }