题目大意
有一个无向图,每次删除一个节点,求删除后图中连通块的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)
题解
连通块?用并查集可以找到一个连通块,但是并查集不支持删除呀!所以我们将删点改为造点并连边就可以用并查集解决这个问题了。
#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;
}