CodeForces Round #678(Div2) E.Complicated Computations Mex性質,權值線段樹
阿新 • • 發佈:2020-10-26
CodeForces Round #678(Div2) E.Complicated Computations Mex性質,權值線段樹
題意
定義\(mex\)表示給定序列中從1開始第一個沒有出現的數
給定一組正整數,問其\(substring\)的\(mex\)的\(mex\)是多少
\[1\leq n \leq 10^5 \\ 1\leq a_i \leq n \]分析
此題和\(HDU-6767\)有點相似,都用到了線段樹和mex的性質,只不過那題似乎比這題麻煩一點
我們考慮先求出所有\(substring\)的\(mex\),如果一個區間的\(mex = a\),那麼容易發現只需滿足如下條件
- 區間中沒有出現\(a\)
- 區間中出現了\(1 到 a - 1\)
想象在考慮\(a\)的時候把整個陣列分割成了若干個不含\(a\)的子段,那麼對於其中一個\(a\),它只會影響到上一個或者下一個\(a\)之後的區間,
這樣就只需要滿足第二個條件,而這個可以用權值線段樹維護
線段樹維護\(a_i\)最後一次的位置,動態維護一顆權值線段樹即可
操作只需要單點修改,區間查詢
注意用\(last\)維護的是上一個位置,因此最後還需掃一遍,來確定後面的貢獻
程式碼
int last[maxn], a[maxn]; int val[maxn << 2]; int vis[maxn]; void update(int i, int l, int r, int pos, int v) { if (l == r) { val[i] = v; return; } int mid = l + r >> 1; if (pos <= mid) update(i << 1, l, mid, pos, v); else update(i << 1 | 1, mid + 1, r, pos, v); val[i] = min(val[i << 1], val[i << 1 | 1]); } int query(int i, int l, int r, int L, int R) { if (l == L && r == R) { return val[i]; } int mid = l + r >> 1; if (R <= mid) return query(i << 1, l, mid, L, R); else if (L > mid) return query(i << 1 | 1, mid + 1, r, L, R); else return min(query(i << 1, l, mid, L, mid), query(i << 1 | 1, mid + 1, r, mid + 1, R)); } int main() { int n = readint(); for (int i = 1; i <= n; i++) a[i] = readint(); for (int i = 1; i <= n; i++) { if (a[i] != 1) vis[1] = 1; if (a[i] > 1 && query(1, 1, n, 1,a[i] - 1) > last[a[i]]) vis[a[i]] = 1; last[a[i]] = i; update(1, 1, n, a[i], i); } for (int i = 2; i <= n + 1; i++) if (query(1, 1, n, 1, i - 1) > last[i]) vis[i] = 1; int res = 1; while (vis[res]) res++; cout << res; }