您的位置:首页 > 其它

Ural1671-Anansi's Cobweb

2016-11-19 11:10 323 查看
给定一个图中有n个点m条边,往图中不断删边并每次输出图被分成了几块。

使用并查集,相互连边的点放到同一个子集中去,这样一来就可以知道图被分成了几块。

但是并查集只能往里面加边却难以删掉一条特定的边,因此先用不会被删除的边建图,遍历并查集可得到删掉所有特定的边后图被分成的块数,然后往里添加最后被删的那条边,不断往前增加,知道添加到第二条被删的边为止。

在增加边时可以先判断边的两个端点u,v是否处在同一个子集内,如果是则子集的数量不变,反之子集的数量减一。

#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>

using namespace std;

const int maxn = 100000 + 5;
const int maxm = 100000 + 5;

struct edge {
int u, v;
};

edge e[maxm];
int del[maxm];
bool mark[maxm];
vector<int> ans;

int par[maxn];
int rk[maxn];

void init(int n) {
for (int i = 0; i < n; i++) {
par[i] = i;
rk[i] = 0;
}
}

int find(int x) {
if (par[x] == x) {
return x;
} else {
return par[x] = find(par[x]);
}
}

void unite(int x, int y) {
x = find(x);
y = find(y);
if (x == y) {
return;
}
if (rk[x] < rk[y]) {
par[x] = y;
} else {
par[y] = x;
if (rk[x] == rk[y]) {
rk[x]++;
}
}
}

bool same(int x, int y) {
return find(x) == find(y);
}

int piece(int n) {
int cnt = 0;
for (int i = 0; i < n; i++) {
if (par[i] == i) {
cnt++;
}
}
return cnt;
}

int main(int argc, char const *argv[]) {
int n, m;
while (scanf("%d%d", &n, &m) == 2) {
for (int i = 0; i < m; i++) {
scanf("%d%d", &e[i].u, &e[i].v);
}
init(n);
int q;
scanf("%d", &q);
for (int i = 0; i < q; i++) {
int x;
scanf("%d", &x);
mark[x-1] = 1;
del[i] = x - 1;
}
for (int i = 0; i < m; i++) {
if (!mark[i]) {
unite(e[i].u, e[i].v);
}
}
int cnt = piece(n);
ans.push_back(cnt);
for (int i = q - 1; i >= 1; i--) {
int x = e[del[i]].u;
int y = e[del[i]].v;
if (!same(x, y)) {
ans.push_back(--cnt);
} else {
ans.push_back(cnt);
}
unite(x, y);
}
reverse(ans.begin(), ans.end());
for (int i = 0; i < ans.size(); i++) {
printf("%d%c", ans[i], i < ans.size() - 1 ? ' ' : '\n');
}
memset(mark, 0, sizeof(mark));
memset(del, 0, sizeof(del));
ans.clear();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: