CF576E 筆記

CTHOOH發表於2024-03-08

前置知識:線段樹分治。

題意

給定一個 \(n\) 個點 \(m\) 個點的圖,有 \(k\) 種顏色,初始時每條邊都沒有顏色。

\(q\) 組詢問,每組詢問 \((i, c)\) 表示把第 \(i\) 條邊顏色改成 \(c\),然後判斷:所有顏色為 \(c\) 的邊拉出來是否是一個二分圖。

如果是就輸出 YES;否則輸出 NO,並撤銷該操作。

\(n, m \le 5 \times 10^5, k \le 50\)

題解

考慮線段樹分治。但是線上段樹分治時,會有一個問題:無法確定一條邊的出現時間。

思考一下線段樹分治的性質,可以發現:當線上段樹上遞迴到葉子結點,並處理當前葉子的詢問時,前面的詢問都被處理過了。就是說,遍歷線段樹,葉節點的詢問一定是 \(1, 2, \cdots, q\) 的順序被遍歷。

於是,考慮同一條邊被改的相鄰的時間節點 \(x, y\),這條邊會影響到的,只有 \([x + 1, y - 1]\) 中的詢問。而遞迴到結點 \([l, r]\) ( \(x < l \le r < y\) ) 時,因為這個時候拿到的邊的顏色是處理了 \([1, l - 1]\) 過後的,所以我們直接連邊即可。總複雜度 \(O(n \log^2 n)\)

程式碼

code:

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int N = 1E6 + 5, K = 51;
int n, m, q, k, a[N], c[N], pre[N], nc[N];
pair <int, int> e[N];
stack <pair <pair <int, int>, int>> st;
struct bcj {
  int fa[N], sz[N];
  void init() {
    for (int i = 1; i <= 2 * n; ++i)
      fa[i] = i, sz[i] = 1;
  }
  int find(int x) {return x == fa[x] ? x : find(fa[x]);}
  void merge(int x, int y, int col) {
    x = find(x), y = find(y);
    if (x == y) return ;
    if (sz[x] > sz[y]) swap(x, y);
    st.emplace(make_pair(make_pair(x, y), col));
    fa[x] = y; sz[y] += sz[x];
  }
  void mer(int id, int col) {
    auto [x, y] = e[id];
    merge(x + n, y, col);
    merge(x, y + n, col);
  }
  void cut(int x, int y) {fa[x] = x; sz[y] -= sz[x];}
  bool same(int x, int y) {return find(x) == find(y);}
} f[K];
void cl(int to) {
  while ((int)st.size() > to) {
    auto [x, y] = st.top().first; int c = st.top().second; st.pop();
    f[c].cut(x, y);
  }
}
vector <int> v[N << 1];
void ins(int x, int l, int r, int L, int R, int p) {
  if (l >= L && r <= R) {
    v[x].emplace_back(p);
    return ;
  } int mid = (l + r) >> 1;
  if (L <= mid) ins(x << 1, l, mid, L, R, p);
  if (R > mid) ins(x << 1 | 1, mid + 1, r, L, R, p);
}
int wtf = 0;
void solve(int x, int l, int r) {
  int to = st.size();
  for (auto id : v[x]) {
    if (nc[id]) f[nc[id]].mer(id, nc[id]);
  }
  int mid = (l + r) >> 1;
  if (l == r) {
    int id = a[l]; auto [u, v] = e[id];
    if (f[c[l]].same(u, v)) cout << "NO\n";
    else cout << "YES\n", nc[id] = c[l];
  } else solve(x << 1, l, mid), solve(x << 1 | 1, mid + 1, r);
  cl(to);
}
signed main(void) {
  ios :: sync_with_stdio(false);
  cin.tie(nullptr); cout.tie(nullptr);
  cin >> n >> m >> k >> q;
  for (int i = 0; i <= k; ++i)
    f[i].init();
  for (int i = 1; i <= m; ++i)
    cin >> e[i].first >> e[i].second;
  for (int i = 1; i <= q; ++i) 
    cin >> a[i] >> c[i];
  for (int i = 1; i <= q; ++i) {
    if (pre[a[i]] + 1 <= i - 1)
      ins(1, 1, q, pre[a[i]] + 1, i - 1, a[i]);
    pre[a[i]] = i;
  } 
  for (int i = 1; i <= m; ++i)
    if (pre[i] < q) ins(1, 1, q, pre[i] + 1, q, i);
  solve(1, 1, q);
}