提交 状态
思路:(标程,有所改动)
这道题考察并查集的逆向思维,先不一一求联通块,先删点和边,最后再用并查集依次逆着加上这些边和点,很好的题目;
/* 按顺序拆点是不好处理的,但是我们可以记录拆点的顺序然后倒过来加点。 每次加点通过并查集来判断连通块数量并记录答案,然后按顺序输出 就行了。先计算拆完点后的连通块数,每次加点将该点和与之相连且 未拆掉的点并入一个集合并判断连通块数。 */ #include<cstdio> #include<algorithm> #include<cstring> #include<vector> using namespace std; const int maxm = 400005; int pre[maxm], a[maxm], flag[maxm] = { 0 }; int sum[maxm] = { 0 }, vis[maxm] = { 0 }; vector<int>v[maxm]; int find(int k) { if (pre[k] == k) return k; return pre[k] = find(pre[k]); } int main() { int n, i, j, k, m, x, y; scanf("%d%d", &n, &m); for (i = ; i <= n; i++) pre[i] = i; for (i = 1; i <= m; i++) { scanf("%d%d", &x, &y); v[x].push_back(y); v[y].push_back(x); } scanf("%d", &k); for (i = 0; i < k; i++) { scanf("%d", &a[i]); flag[a[i]] = 1; } int t1, t2; for (i = 1; i <= n; i++) { //逆着加边,这是最后剩下的所有边,构建图 if (flag[i]) continue; for (j = 0; j < v[i].size(); j++) { if (flag[v[i][j]]) continue; t1 = find(i), t2 = find(v[i][j]); if (t1 != t2) pre[t1] = t2; } } for (i = 1; i <= n; i++) { //最后剩下的连通块 if (flag[i]) continue; int xx = find(i); if (!vis[xx]) { sum[k]++; vis[xx] = 1; } } int rev; for (i = k - 1; i >= 0; i--) { flag[a[i]] = 0; sum[i] = sum[i + 1]; rev = 0; for (j = 0; j < v[a[i]].size(); j++) { if (flag[v[a[i]][j]]) continue; rev++; t1 = find(a[i]), t2 = find(v[a[i]][j]); if (t1 != t2) { if (t1 != a[i]) sum[i]--; pre[t1] = t2; } } if (rev == 0) sum[i]++; } for (i = 0;i <= k;i++) printf("%d\n", sum[i]); return 0; }我来粘一发TLE代码( 果然是菜,想蹭弱数据,没想到逆向思维): #include <cstdio> #include <cstring> #include <algorithm> #include <cmath> #include <queue> #define max_n 400010 using namespace std; int cnt, n, m, k, v, res; int head[max_n], vis[max_n], gg[max_n]; bool flag = true; struct node { int to; int next; }edge[max_n]; void addedge(int u, int w) { edge[cnt].to = w; edge[cnt].next = head[u]; head[u] = cnt++; } void bfs(int x) { if(!flag) gg[x] = 1; memset(vis, 0, sizeof(vis)); for(int i = 1; i <= n; i++) { if(gg[i] || vis[i]) continue; res++; queue<int> q; q.push(i); while(!q.empty()) { cnt = q.front(); q.pop(); if(gg[cnt] || vis[cnt]) continue; vis[cnt] = 1; int u = head[cnt]; for(int j = u; j != -1; j = edge[j].next) { q.push(edge[j].to); } } } } int main() { int a, b; memset(head, -1, sizeof(head)); memset(gg, 0, sizeof(gg)); scanf("%d %d", &n, &m); for(int i = 0; i < m; i++) { scanf("%d %d", &a, &b); addedge(a, b); addedge(b, a); } res = 0, bfs(1); printf("%d\n",res); flag = false; scanf("%d", &k); while(k--) { memset(vis, 0, sizeof(vis)); scanf("%d", &v); res = 0; bfs(v); printf("%d\n",res); } return 0; }