1. 程式人生 > >【做題】CERC2017B. Buffalo Barricades——時間倒流

【做題】CERC2017B. Buffalo Barricades——時間倒流

使用 times 其中 數據結構 getchar cer void 或操作 表示

原文鏈接 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——時間倒流