Codeforces 1132G(關系轉化樹+dfn+線段樹)
阿新 • • 發佈:2019-04-12
oid i++ push_back fine 必須 cto dfs序 back force
要點
- 顯然要滑動修改維護。
- 像通常的數列next關系一樣建邊(單調棧預處理),因為貪心所以是樹,然後發現增刪只會影響區間內的子(or父,看你連邊方向行事)節點,於是使用dfs序建線段樹。
- 為了正確地修改,會發現必須得用大數向小數連邊。一是根據題意,一個大數會有好幾個小數兒子但小數只會貪心選一個父親;二是每當增加一個大數時,對每個子孫都會有一個貢獻,正好我們用size數組搞定左右邊界。
- 某些子孫已經被滑動窗口移除了,加進一個大數又給它加了1,不會影響嗎?不影響答案,因為移除以後它是0,只會有一個父親出現才會增加它為1,並不影響最終答案。
const int maxn = 1e6 + 5; int n, k, a[maxn], dfn[maxn], Time, size[maxn]; vector<int> adj[maxn]; class SegmentTree { public: #define ls(p) p << 1 #define rs(p) p << 1 | 1 struct Node { int l, r, maxx, tag; }t[maxn << 2]; void Push_up(int p) { t[p].maxx = max(t[ls(p)].maxx, t[rs(p)].maxx); } void Push_down(int p) { if (t[p].tag) { t[ls(p)].maxx += t[p].tag, t[rs(p)].maxx += t[p].tag; t[ls(p)].tag += t[p].tag, t[rs(p)].tag += t[p].tag; t[p].tag = 0; } } void Build(int l, int r, int p) { t[p].l = l, t[p].r = r; if (l == r) return; int mid = (l + r) >> 1; Build(l, mid, ls(p)); Build(mid + 1, r, rs(p)); } void Modify(int l, int r, int p, int k) { if (l <= t[p].l && t[p].r <= r) { t[p].maxx += k, t[p].tag += k; return; } Push_down(p); int mid = (t[p].l + t[p].r) >> 1; if (l <= mid) Modify(l, r, ls(p), k); if (mid < r) Modify(l, r, rs(p), k); Push_up(p); } }T; void dfs(int fa) { dfn[fa] = ++Time; size[fa] = 1; for (auto i : adj[fa]) { dfs(i); size[fa] += size[i]; } } void Pre() { stack<int> st; irep(i, n, 1) { while (!st.empty() && a[st.top()] <= a[i]) st.pop(); int fa = st.empty() ? n + 1 : st.top(); adj[fa].push_back(i); st.push(i); } dfs(n + 1); } void Solve() { T.Build(1, n + 1, 1); rep(i, 1, k - 1) T.Modify(dfn[i], dfn[i] + size[i] - 1, 1, 1); for (int i = 1; i + k - 1 <= n; i++) { T.Modify(dfn[i + k - 1], dfn[i + k - 1] + size[i + k - 1] - 1, 1, 1); printf("%d ", T.t[1].maxx); T.Modify(dfn[i], dfn[i] + size[i] - 1, 1, -1); } } int main() { read(n), read(k); rep(i, 1, n) read(a[i]); Pre(); Solve(); return 0; }
Codeforces 1132G(關系轉化樹+dfn+線段樹)