luogu1197 [JSOI2008]星球大战
题目大意
有一个无向图,每次删除一个节点,求删除后图中连通块的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)
题解
连通块?用并查集可以找到一个连通块,但是并查集不支持删除呀!所以我们将删点改为造点并连边就可以用并查集解决这个问题了。
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int MAX_EDGE = 200010, MAX_NODE = MAX_EDGE * 2; int TotNode, TotEdge; struct Node; struct Edge; struct Edge { Node *To; Edge *Next; }_edges[MAX_EDGE * 2]; int _eCount; struct Node { Node *Father; Edge *Head; int AddRank; bool Built; }_nodes[MAX_NODE], *AddOrder[MAX_NODE]; int Ans[MAX_NODE]; void AddEdge(Node *from, Node *to) { Edge *e = _edges + _eCount++; e->To = to; e->Next = from->Head; from->Head = e; } void Build(int uId, int vId) { AddEdge(_nodes + uId, _nodes + vId); AddEdge(_nodes + vId, _nodes + uId); } Node *FindRoot(Node *cur) { return cur->Father == cur ? cur : cur->Father = FindRoot(cur->Father); } void Init(int n) { for (int i = 0; i < n; i++) _nodes[i].Father = _nodes + i; } bool Join(Node *a, Node *b) { Node *root1 = FindRoot(a), *root2 = FindRoot(b); if (root1 == root2) return false; else { root1->Father = root2; return true; } } bool Cmp(Node *a, Node *b) { return a->AddRank < b->AddRank; } int main() { scanf("%d%d", &TotNode, &TotEdge); for (int i = 0; i < TotEdge; i++) { int u, v; scanf("%d%d", &u, &v); Build(u, v); } int attackCnt; scanf("%d", &attackCnt); for (int i = 0; i < attackCnt; i++) { int v; scanf("%d", &v); _nodes[v].AddRank = attackCnt - i; } for (int i = 0; i < TotNode; i++) AddOrder[i] = _nodes + i; sort(AddOrder, AddOrder + TotNode, Cmp); int setCnt = 0; Init(TotNode); for (int i = 0; i < TotNode; i++) { AddOrder[i]->Built = true; setCnt++; for (Edge *e = AddOrder[i]->Head; e; e = e->Next) if (e->To->Built) setCnt -= Join(AddOrder[i], e->To); Ans[i]=setCnt; } for (int i = TotNode - 1; i >= TotNode - attackCnt - 1; i--) printf("%d\n", Ans[i]); return 0; }