【BZOJ 1015】 [JSOI2008]星球大战starwar
【题目链接】:http://www.lydsy.com/JudgeOnline/problem.php?id=1015
【题意】
【题解】
/*
可以倒过来考虑;
相当于一个加入了一个节点;
然后问你联通块的数量;
可以记录所有节点有哪些边和它是连在一起的;
然后查询新加入的节点的边的另外一端是哪些节点;
之后的操作会让这些节点连起来;
看看边的另外一端的点对应的不同联通块有多少个->x;
有多少个联通块就减去x-1;
(具体实现的时候,先让这个新加入的节点和相邻的点尝试连起来,如果
它们之前是不连通的那么计数器num递增,然后联通块的个数now=now+1-num);
用并查集吧。
(一开始联通块的个数为n-k哦)
*/
【完整代码】
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
using namespace std;
#define rei(x) scanf("%d",&x)
#define rep2(i,y,x) for (int i = y;i >= x;i--)
#define rep1(i,x,y) for (int i = x;i <= y;i++)
const int N = 4e5 + 1000;
const int M = 2e5 + 1000;
struct abc
{
int x, y;
};
int n, m,ans[N],now;
int f[N],k,a[N];
bool bo[N];
vector <int> g[N];
abc bian[M];
int ff(int x)
{
if (f[x] == x) return x;
else
return f[x] = ff(f[x]);
}
int main()
{
//freopen("D:\\rush.txt", "r", stdin);
//freopen("D:\\rush_out.txt", "w", stdout);
rei(n),rei(m);
rep1(i, 1, m)
{
int x, y;
rei(x), rei(y);
g[x].push_back(y),g[y].push_back(x);
bian[i].x = x, bian[i].y = y;
}
memset(bo, true, sizeof bo);
rei(k);
rep1(i, 1, k)
{
rei(a[i]);
bo[a[i]] = false;
}
rep1(i, 0, n-1)
f[i] = i;
now = n-k;
rep1(i, 1, m)
{
int x, y;
x = bian[i].x, y = bian[i].y;
if (!bo[x] || !bo[y]) continue;
int r1 = ff(x), r2 = ff(y);
if (r1 != r2)
{
f[r1] = r2;
now--;
}
}
rep2(i, k, 1)
{
ans[i] = now;
int x = a[i],num = 0;
bo[x] = true;
int len = g[x].size();
now++;
rep1(j, 0, len - 1)
{
int y = g[x][j];
if (!bo[y]) continue;
int r1 = ff(y),r2 = ff(x);
if (r1 != r2)
{
f[r1] = r2;
num++;
}
}
now -= num;
}
ans[0] = now;
rep1(i, 0, k)
printf("%d\n", ans[i]);
return 0;
}