[CF Contest] Web of Lies 題解
阿新 • • 發佈:2021-08-03
題目描述
給你一個 \(n\) 個點 \(m\) 條邊的無向圖,要求支援以下幾種操作共 \(q\) 次:
- 加入一條邊 \((u,v)\)。
- 刪除一條邊 \((u,v)\)。
- 查詢按照以下步驟對圖進行操作後剩下的點的數量:刪除所有與其相連的點編號都比其大的點和與該點相連的邊,然後再次進行操作,直到刪除點的數量為 \(0\) 為止。(該查詢對圖的結構不影響)
資料範圍:\(1 \le n,q \le 2\cdot 10^5, 0 \le m \le 2\cdot 10^5\) 。
結論
對於每一個點,只要有任意相鄰的點編號大於它,那麼該點一定會在最後一輪前被刪除。
證明
採用反證法。假如一個有一個相鄰點編號大於它,且其存活到了最後一輪。那麼其必有一個相鄰點小於它。其相鄰點若沒有編號比其更小的,那麼其會在本輪被刪除,那本輪不為最後一輪。其相鄰點若有編號比其更小的,同樣的邏輯也適用於該點。這樣我們得到一個無限下降的論證,但編號小於起始點的點數量是有限的,所以產生了矛盾。得證。
程式碼實現
由於正常鄰接表的邊刪除操作不是 \(O(1)\) 的,所以不能使用鄰接表。
我們可以考慮使用一個叫 alive
的陣列來儲存 “比編號為 \(i\) 的點編號大的其鄰居的數目”,再使用一個變數 ans
儲存每次詢問的答案,由於初始所有點都是不連通的,那麼 ans
初始化為 \(n\)。
- 假如一個點加邊後有且僅有一個相鄰的點編號大於它,答案就減少 \(1\)。
- 假如一個點刪掉與它相鄰且唯一的編號大於它的點之間的邊,答案就增加 \(1\)。
程式碼
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <vector> using namespace std; const int maxn = 2e5 + 5; int alive[maxn], n, m; int main() { cin >> n >> m; int ans = n; for(int i = 1, x, y; i <= m; i ++) { cin >> x >> y; if(x > y) { alive[y] ++; if(alive[y] == 1) ans --; } else { alive[x] ++; if(alive[x] == 1) ans --; } } int q; cin >> q; while(q --) { int cmd, x, y; cin >> cmd; if(cmd == 1) { cin >> x >> y; if(x > y) { // when x is stronger than y alive[y] ++; // y's dangerous friend ++ if(alive[y] == 1) ans --; // if this is the first dangerous friend, answer --; } else { alive[x] ++; if(alive[x] == 1) ans --; } } else if(cmd == 2) { cin >> x >> y; if(x > y) { alive[y] --; if(alive[y] == 0) ans ++; } else { alive[x] --; if(alive[x] == 0) ans ++; } } else { cout << ans << endl; } } }