前置知識:線段樹分治。
題意
給定一個 \(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);
}