【2020ICPC·小米 網路選拔賽第一場-E】Phone Network
阿新 • • 發佈:2020-10-26
題目連結:https://ac.nowcoder.com/acm/contest/7501/E
題目大意
給定\(n\)個數的序列,對於每一個\(m\),求最短的涵蓋從\(1\)到\(i\)的線段的長度。
思路
看了題解之後只剩下:妙啊。
題解已經說得很清楚了,這裡復讀一遍重新整理一下自己的思路。
\(R_{i,l}\)代表以\(l\)為線段左端點,包含數從\(1\)~\(i\)中所有數字的最近右端點。
對於\(i+1\)來說,其所在的位置為\(p_{1},p_{2},p_{3},...,p_{k}\)。這些數將長度為\(n\)的區間劃分為若干部分,如下所示:
[1, \(p_{1}\)],[\(p_{1}+1\)
對於每一個區間,其內部所有的 \(l\) 在從 \(R_{i,l}\) 轉移至 \(R_{i+1,l}\) 時,式子為\(R_{i+1,l}\) = \(max(R_{i,l}, \ p_{k})\)
其中,\(p_{k}\)代表其所在區間的右端點。
特別的,當所在的 \(l\) 位於最後一個線段中,不可能構成包含數從 \(1\)~\(i\) 中所有數字的線段,此時應將 \(R_{i+1,l}\) 置為無窮大,也就是取不到
顯然在任何情況下 \(R_{i,l}\) 是單調遞增的。最後,將問題轉移成了對於每一個區間,找到 \(R_{i,l} < p_{k}\)
線段樹維護過程:
用結構體儲存線段樹每個節點的左端點和右端點,當被賦值時,因為時區間同時賦值,因此所在區間的最短線段的長度記為 \(val - T[rt].r + 1\) 。
在查詢 \(R_{i,l} < p_{k}\) 的左端點時儘可能地往左邊走,在查詢右端點時儘可能地往右邊走,如果整個區間內沒有符合的情況則跳過賦值。
AC程式碼
#include <bits/stdc++.h> #define SZ(x) (int)x.size() #define pii pair<int, int> #define mp make_pair #define inf 0x3f3f3f3f #define pb push_back using namespace std; const int MAXN = 2e5 + 5; int init_val[MAXN]; class SEG { public: struct node { int l, r; int val, minn; // val是維護的右端點,minn是線段長度 } T[MAXN << 2]; int lazy[MAXN << 2]; inline void push_up(int rt) { T[rt].minn = min(T[rt << 1].minn, T[rt << 1 | 1].minn); T[rt].val = min(T[rt << 1].val, T[rt << 1 | 1].val); } void build(int rt, int l, int r) { T[rt].l = l, T[rt].r = r; if (l == r) { T[rt].minn = init_val[l] - r + 1; T[rt].val = init_val[l]; return; } int mid = (l + r) >> 1; build(rt << 1, l, mid), build(rt << 1 | 1, mid + 1, r); push_up(rt); } inline void push_down(int rt) { if (lazy[rt]) { T[rt << 1].val = lazy[rt], T[rt << 1 | 1].val = lazy[rt]; T[rt << 1].minn = lazy[rt] - T[rt << 1].r + 1, T[rt << 1 | 1].minn = lazy[rt] - T[rt << 1 | 1].r + 1; lazy[rt << 1] = lazy[rt], lazy[rt << 1 | 1] = lazy[rt]; lazy[rt] = 0; } } int query_left(int rt, int L, int R, int v) { if (T[rt].r < L || T[rt].l > R) return -1; if (T[rt].l == T[rt].r) return T[rt].l; push_down(rt); int ans = -1; if (T[rt << 1].val < v) ans = query_left(rt << 1, L, R, v); if (ans == -1 && T[rt << 1 | 1].val < v) ans = query_left(rt << 1 | 1, L, R, v); return ans; } int query_right(int rt, int L, int R, int v) { if (T[rt].r < L || T[rt].l > R) return -1; if (T[rt].l == T[rt].r) return T[rt].l; push_down(rt); int ans = -1; if (T[rt << 1 | 1].val < v) ans = query_right(rt << 1 | 1, L, R, v); if (ans == -1 && T[rt << 1].val < v) ans = query_right(rt << 1, L, R, v); return ans; } void change(int rt, int L, int R, int v) { if (L <= T[rt].l && T[rt].r <= R) { T[rt].val = v; T[rt].minn = v - T[rt].r + 1; lazy[rt] = v; return; } push_down(rt); int mid = (T[rt].l + T[rt].r) >> 1; if (L <= mid) change(rt << 1, L, R, v); if (R > mid) change(rt << 1 | 1, L, R, v); push_up(rt); } } tree; int a[MAXN]; vector<int> vec[MAXN]; int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); for (int i = 1; i <= n; i++) vec[a[i]].pb(i); int pos = inf; for (int i = n; i >= 1; i--) { if (a[i] == 1) pos = i; init_val[i] = pos; } tree.build(1, 1, n); printf("%d", tree.T[1].minn); // m = 1 for (int i = 2; i <= m; i++) { for (int j = 0; j < SZ(vec[i]); j++) { int l, r, p = vec[i][j]; if (j == 0) { // segment no 1 l = 1, r = vec[i][j]; } else l = vec[i][j - 1] + 1, r = vec[i][j]; int left = tree.query_left(1, l, r, p); int right = tree.query_right(1, l, r, p); if (left == -1 && right == -1) { } else { tree.change(1, left, right, p); } } if (vec[i][SZ(vec[i]) - 1] == n) {} else { tree.change(1, vec[i][SZ(vec[i]) - 1] + 1, n, inf); // 忘記+1導致debug了好久 } printf(" %d", tree.T[1].minn); } }
附上一組樣例:
10 4
1 1 2 3 1 2 1 4 1 4