數列找不同(莫隊)
阿新 • • 發佈:2021-07-13
問題分析
資料應該比較水。這裡當作普通莫隊的模板題。
思路
將 \(N\) 個整數分塊,每塊大小為 \(S\) 。將所有詢問離線,並排序。左端點在不同塊的,左端點小的在前,否則右端點小的在前。
之後每個詢問按順序一個個做。求出 \((l,r)\) 之後,可以花費 \(O(1)\) 轉移到 \((l-1,r),(l+1,r),(l,r+1),(l,r-1)\) 。
複雜度證明
排序複雜度為 \(O(m \log m)\)
將左右兩個端點分開考慮。對於左端點,由於排過序的關係,從一個轉移到下一個最多走 \(S\) 步, 為 \(O(S)\) ;對於右端點,在每個塊(按左端點所在的塊算)內有序,所以一個塊最多花費 \(O(n)\)
於是求答案的時間複雜度為 \(O(m \times S + n\times \frac{n}{S})\) 。在本題 \(n,m\) 同階的情況下取 \(S=\sqrt{n}\) ,時間複雜度為 \(O(n\sqrt{n})\) 。
參考程式
#include <bits/stdc++.h> using namespace std; const int Maxn=100010; int N, Q, A[Maxn], Block; struct query { int L, R, l, r, Ind; inline void Gen(int _Ind) { l = (L + Block - 1) / Block; r = (R + Block - 1) / Block; Ind = _Ind; return; } } Query[Maxn]; int Ans[Maxn]; int L, R, Flag, Cnt[Maxn]; inline bool Cmp(query x, query y) { if (x.l < y.l) return true; if (x.l > y.l) return false; if (x.R < y.R) return true; return false; } int main() { scanf("%d%d", &N, &Q); Block = int(sqrt(N)); for (int i = 1; i <= N; ++i) scanf("%d", &A[i]); for (int i = 1; i <= Q; ++i) scanf("%d%d", &Query[i].L, &Query[i].R); for (int i = 1; i <= Q; ++i) Query[i].Gen(i); sort(Query + 1, Query + Q + 1, Cmp); // for (int i = 1; i <= Q; ++i) printf("%d %d\n", Query[i].L, Query[i].R); L = 1; R = 0; Flag = 0; for (int i = 1; i <= Q; ++i) { while (R < Query[i].R) if (++Cnt[A[++R]] == 2) ++Flag; while (R > Query[i].R) if (--Cnt[A[R--]] == 1) --Flag; while (L < Query[i].L) if (--Cnt[A[L++]] == 1) --Flag; while (L > Query[i].L) if (++Cnt[A[--L]] == 2) ++Flag; // for (int j = 1; j <= N; ++j) printf("%d ", Cnt[j]); printf("\n"); if (!Flag) Ans[Query[i].Ind] = 1; } for (int i = 1; i <= Q; ++i) if (Ans[i]) printf("Yes\n"); else printf("No\n"); return 0; }