【洛谷P1197】星球大战【并查集】
题目大意:
题目链接:https://www.luogu.org/problemnew/show/P1197
给出一张图,每次删除一个点(以及连接它的边),求每次删除后的连通块个数。
思路:
时间倒流应该是很显然的吧。
由于并查集的删除操作并不好搞,所以可以考虑反过来,把“删除”变成“建造”。
首先用记录每一个点连接的边。不需要用领接表,因为每条边只需访问1次。用会MLE。
然后用记录需要建造的点,把不用建造的点之间的边连起来。
之后依次枚举建造的点,处理一下与其相连的点,如果不在同一集合就合并,并且连通块数量减1。
这样的时间复杂度就是
代码:
#include <cstdio>
#include <vector>
using namespace std;
const int N=400010;
int n,m,x,y,father[N],ans[N],a[N];
bool used[N];
vector<int> q[N];
int find(int x)
{
return x==father[x]?x:father[x]=find(father[x]);
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
q[x+1].push_back(y+1);
q[y+1].push_back(x+1);
}
scanf("%d",&m);
for (int i=1;i<=n;i++)
father[i]=i;
for (int i=1;i<=m;i++)
{
scanf("%d",&a[i]);
a[i]++;
used[a[i]]=1;
}
ans[0]=n-m;
for (int i=1;i<=n;i++)
if (!used[i])
{
while (q[i].size())
{
y=q[i].back();
if (find(y)!=find(i)&&!used[y])
{
father[find(y)]=find(i);
ans[0]--;
}
q[i].pop_back();
}
}
for (int i=1;i<=m;i++)
{
x=a[m-i+1];
ans[i]=ans[i-1]+1;
while (q[x].size())
{
y=q[x].back();
if (find(y)!=find(x)&&!used[y])
{
father[find(y)]=find(x);
ans[i]--;
}
q[x].pop_back();
}
used[x]=0;
}
for (int i=m;i>=0;i--)
printf("%d\n",ans[i]);
return 0;
}