1. 程式人生 > 其它 >Mayor's posters

Mayor's posters

Mayor's posters

線段樹+離散化

將海報覆蓋的區間線上段樹中進行標記,最後統計存在於樹中的海報型別

由於本題的資料範圍比較大,需要對座標進行離散化。之後再遞迴的遍歷每一個節點,將存在的標記存放到set中,最終set中元素的數量就是海報的型別

注意

  1. 離散化後,區間之間的相對寬度會發生變化,比如[1, 10000]離散化後,變成[1,2],顯然後者的覆蓋範圍要遠小於前者。這點要注意,所以格外新增\(l+1\)\(r+1\)
  2. 除了建樹的過程外,對於訪問子節點的操作都需要下放懶標記,所以在修改和詢問時要push_down
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>

using namespace std;

typedef pair<int, int> PII;
constexpr int N = 2e5 + 100;

struct Node {
  int l, r;
  int t;
} tr[N * 4];

int n, g[N], cnt;
vector<PII> v(N);
set<int> se;

inline int get(int x) {
  return lower_bound(g + 1, g + cnt + 1, x) - g;
}

void build(int u, int l, int r) {
  tr[u] = {l, r};
  if (l == r) return;
  int mid = l + r >> 1;
  build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}

inline void push_down(int u) {
  int &t = tr[u].t;
  if (t) tr[u << 1].t = tr[u << 1 | 1].t = t;
  t = 0;
}

void modify(int u, int l, int r, int t) {
  if (tr[u].l >= l && tr[u].r <= r) tr[u].t = t;
  else {
    push_down(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid) modify(u << 1, l, r, t);
    if (r > mid) modify(u << 1 | 1, l, r, t);
  }
}

void count(int u, int l, int r) {
  // printf("%d %d %d %d\n", u, tr[u].t, l, r);
  if (tr[u].t) se.insert(tr[u].t);
  if (l == r) return;
  push_down(u);
  int mid = l + r >> 1;
  count(u << 1, l, mid), count(u << 1 | 1, mid + 1, r);
}

int main() {
  int _T;
  scanf("%d", &_T);
  while (_T --) {
    scanf("%d", &n);
    v.clear();
    se.clear();
    cnt = 0;

    for (int i = 0; i < n; i++) {
      int l, r; scanf("%d%d", &l, &r);
      v.push_back({l, r});
      g[++cnt] = l, g[++cnt] = r, g[++cnt] = r + 1, g[++cnt] = l + 1;
    }
    sort(g + 1, g + cnt + 1);
    cnt = unique(g + 1, g + cnt + 1) - g;
    build(1, 1, cnt);
    
    for (int i = 0; i < v.size(); i++) {
      auto [l, r] = v[i];
      l = get(l), r = get(r);
      modify(1, l, r, i + 1);
    }
    count(1, 1, cnt);
    
    printf("%zd\n", se.size());
  }
  
  return 0;
}

set非離散化

從後往前,將當前海報覆蓋的範圍的點全部刪除,列舉的區間存在部分端點在set中,代表該海報沒有被完全覆蓋

注意:假如要刪除的區間是最右邊的,那麼當區間內的元素會始終小於區間右邊端點,此時while迴圈不會停止,所以要格外假如r+1這個點。

#include <iostream>
#include <algorithm>
#include <set>
#include <cstdio>
#include <utility>
using namespace std;
typedef pair<int, int> PII;

const int N = 1e5 + 1000;

set<int> se;
PII q[N];
int n;

int main() {
  int _T;
  scanf("%d", &_T);
  
  while (_T --) {
    int res = 0;
    se.clear();
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
      int & l = q[i].first, &r = q[i].second;
      scanf("%d%d", &l, &r);
      se.insert(l), se.insert(r), se.insert(r + 1);
    }
    
    for (int i = n; i; i--) {
      int l = q[i].first, r = q[i].second;
      auto t = se.lower_bound(l);
      if (*t <= r) res++;
      
      while (*t <= r) {
        se.erase(t);
        t = se.lower_bound(l);
      }
    }
    printf("%d\n", res);
  }
  return 0;
}