【问题描述】

  FJ和他的奶牛们正在计划离开小镇做一次长的旅行,同时FJ想临时地关掉他的农场以节省一些金钱。

  这个农场一共有被用 M 条双向道路连接的 N 个谷仓。为了关闭整个农场,FJ 计划每一次关闭掉一个谷仓。

当一个谷仓被关闭了,所有的连接到这个谷仓的道路都会被关闭,而且再也不能够被使用。

  FJ现在正感兴趣于知道在每一个时间(这里的“时间”指在每一次关闭谷仓之前的时间)时他的农场是否是“全连通的”——也就是说从任意的一个开着的谷仓开始,能够到达另外的一个谷仓。注意自从某一个时间之后,可能整个农场都开始不会是“全连通的”。

【输入格式】

  第 1 行是 N 和 M ;
  接下来的 M 行,每行包含两个整数,表示一条双向道路连接的的两个谷仓。
  最后的 N 行,每行是一个整数,表示本次关闭的谷仓。

【输出格式】

  输出 N 行,其中第 i 行输出,如果关闭谷仓 i 全连通,则输出 Yes,否则输出 No。

【输入输出样例】

  Input
4 3
1 2
2 3
3 4
3
4
1
2
 Output
YES
NO
YES
YES

【数据说明】

  对于 100% 的数据 1≤N,M≤3000。

思路:

这里将谷仓一个个关闭,但是我们可以反其道而行之,将谷仓的开放顺序储存下来,然后,逆序进行判断,将要删去的点一个个加进来,遍历其所连的边上的点。

如果所连的点已经被加入了(开放了),同时两个点不属于同一个集合,我们就把两个集合合并,同时用tot储存集合内点的数量,当tot=n-i时就说明这个图是全联通的。
#include<bits/stdc++.h>
using namespace std;
const int maxn=10005;
int n,m;
vector<int> e[maxn];
int vis[maxn];
int k[maxn];
int pa[maxn];
bool ans[maxn];
void makeset()
{
    for(int i=1;i<=n;i++)
    {
        pa[i]=i;
    }
}
int Find(int x)
{
    int r=x;
    while(pa[r]!=r)
    r=pa[r];
    return r;
}

int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        int a,b;
        cin>>a>>b;
        e[a].push_back(b);
        e[b].push_back(a);

    }
    for(int i=1;i<=n;i++)
    {
        cin>>k[i];
    }
    makeset();
    int tot=0;
    for(int i=n;i>=1;i--)
    {
        vis[k[i]]=1;
        int x=Find(k[i]);

        for(int j=0;j<e[x].size();j++)
        if(vis[e[x][j]]==1)
        {
            int y=Find(e[x][j]);
            if(x!=y)
            {
                tot++;
                pa[y]=x;
            }
        }
        if(tot==n-i)
        ans[i]=1;
    }
    for(int i=1;i<=n;i++)
    {
        if(ans[i]==1)
        printf("YES\n");
        else
        printf("NO\n");
    }
    return 0;
}
12-15 07:29