【洛谷P1197】星球大战【并查集】

题目大意:

题目链接:https://www.luogu.org/problemnew/show/P1197
给出一张图,每次删除一个点(以及连接它的边),求每次删除后的连通块个数。


思路:

时间倒流应该是很显然的吧。
由于并查集的删除操作并不好搞,所以可以考虑反过来,把“删除”变成“建造”。
首先用vectorvector记录每一个点连接的边。不需要用领接表,因为每条边只需访问1次。用queuequeue会MLE。
然后用usedused记录需要建造的点,把不用建造的点之间的边连起来。
之后依次枚举建造的点,处理一下与其相连的点,如果不在同一集合就合并,并且连通块数量减1。
这样的时间复杂度就是O(m×α(n))O(m\times \alpha(n))


代码:

#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;
} 
posted @ 2019-04-25 16:33  全OI最菜  阅读(103)  评论(0编辑  收藏  举报