【做題】CERC2017B. Buffalo Barricades——時間倒流
原文鏈接 https://www.cnblogs.com/cly-none/p/CERC2017B.html
題意:在一個網格平面上,有\(n\)個點,其中第\(i\)個點在以\((x_i, y_i)\)為右上角的網格中。有\(m\)次操作,每次給出一個點\((x,y)\),表示從\((x,y)\)開始,向左和向下畫線直到與之前畫的線或坐標軸相交。這樣會劃分出以\((x,y)\)為右上角的新區域。你需要對每次操作求出,新區域中點的數量。
$n , m \leq 3 \times 10^5, ? 1 \leq x_i, y_i \leq 10^9, $ 每次操作的 \(x\) 和 \(y\) 分別互不相同。
首先,我們容易得出各種\(O(n \log ^ 2n)\)的數據結構做法。然而,它們並沒有太大的啟發意義。
考慮離線下來,處理每個點對所有操作的貢獻。
考慮求出每個點和操作最後是被哪個操作控制的。也就是能直接覆蓋這個點(或操作)的操作中時間最早的。這樣會形成森林的結構。顯然,一個點只會對它的祖先產生貢獻。而它對某個祖先\(v\)產生貢獻的沖要條件就是在它到\(v\)的路徑上沒有比\(v\)更早的操作。
求這個森林可以使用掃描線。按\(y\)從上往下掃描,每加入一個操作就刪去在它前面的出現時間晚於它的操作。這可以用set來維護。
剩下的部分可以按時間從前往後枚舉所有操作,每次答案就是它的子樹和,然後切斷它與父親的邊。方便起見,可以用時間倒流的技巧,就相當與每次合並一個點和它的父親。用並查集維護即可。
時間復雜度\(O(n \log n)\)。
#include <bits/stdc++.h> using namespace std; #define gc() getchar() template <typename tp> inline void read(tp& x) { x = 0; char tmp; bool key = 0; for (tmp = gc() ; !isdigit(tmp) ; tmp = gc()) key = (tmp == '-'); for ( ; isdigit(tmp) ; tmp = gc()) x = (x << 3) + (x << 1) + (tmp ^ '0'); if (key) x = -x; } const int N = 300010, INF = 0x3f3f3f3f; struct edge { int la,b; } con[N << 1]; int tot,fir[N << 1]; void add(int from,int to) { con[++tot] = (edge) {fir[from],to}; fir[from] = tot; } struct data { int x,y,v; bool operator < (const data& a) const { return y > a.y; } } dat[N << 1]; int n,m,cnt,fa[N],val[N],uni[N],ans[N]; int getfa(int pos) { return pos == uni[pos] ? pos : uni[pos] = getfa(uni[pos]); } typedef pair<int,int> pii; set<pii> st; set<pii> :: iterator t, t1; void ins(int x,int y) { t = st.insert(pii(x,y)).first; while (t != st.begin()) { t1 = t; -- t1; if (t1 -> second < y) break; st.erase(t1); } } int ask(int p) { return st.upper_bound(pii(p+1,0))->second; } int main() { read(n); for (int i = 1, x, y ; i <= n ; ++ i) { read(x), read(y); -- x, -- y; dat[++cnt] = (data) {x,y,0}; } read(m); for (int i = 1, x, y ; i <= m ; ++ i) { read(x), read(y); dat[++cnt] = (data) {x,y,i}; } dat[++cnt] = (data) {INF,INF,m+1}; sort(dat+1,dat+cnt+1); for (int i = 1, las = 1, tmp ; i <= cnt ; ++ i) { if (dat[i].y != dat[las].y) { for (int j = las ; j < i ; ++ j) if (dat[j].v) ins(dat[j].x, dat[j].v); las = i; } tmp = ask(dat[i].x); if (dat[i].v) fa[dat[i].v] = tmp; else ++ val[tmp]; } for (int i = 1 ; i <= m ; ++ i) uni[i] = i; for (int i = m ; i >= 1 ; -- i) { ans[i] = val[getfa(i)]; uni[getfa(i)] = getfa(fa[i]); val[getfa(fa[i])] += ans[i]; } for (int i = 1 ; i <= m ; ++ i) printf("%d\n",ans[i]); return 0; }
小結:這是一道技巧性比較強的數據結構題。利用詢問間的關系解題的思路,還是有啟發意義的。
【做題】CERC2017B. Buffalo Barricades——時間倒流