【做题】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;
}


小结:这是一道技巧性比较强的数据结构题。利用询问间的关系解题的思路,还是有启发意义的。

posted @ 2019-01-19 13:35 莫名其妙的aaa 阅读(...) 评论(...) 编辑 收藏