洛谷P3144 [USACO16OPEN]关闭农场Closing the Farm

农夫约翰和他的奶牛准备去旅行,所以约翰想要把他的农场临时关闭。

农场有N个牛棚(牛棚从1到N编号),有M条路连接这些牛棚(1≤N,M≤3000)。

约翰打算挨个关闭牛棚,在关牛棚的时候,

他突然想起一个有趣的问题:剩余的这些没有关闭的牛棚是不是连通呢?

连通指的是从任何一个牛棚出发,都能到达其他牛棚(注意:已经关闭的牛棚不可以通行)。

Input

第一行包括两个整数N M,

接下来M行,每行输入两个整数x y,表示x和y牛棚之间存在一条路,路是双向通行的。

接下来n行,表示关牛棚的顺序。

Output 输出N行:

第一行表示初始状态下,牛棚是否连通;

接下来N-1行,表示关闭对应牛棚后,剩余牛棚是否联通。

如果连通,输出YES,不连通,输出NO。(最后一次关闭不需要输出)。

————————————————————————————————————————

回来补一波并查集

这里我们做一波离线处理 将删点转换为加点 也就是将关闭变为开启

那么我们每次开启一个牛棚x  k++(k指的是当前联通快的个数)

然后我们枚举x的所有出路 找到已经开始的牛棚 如果他们不在一个了联通块内k-- 合并两个块

最后如果这个时刻k是1 那么就是联通 否则不是

#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int M=30007;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+(c-'0'); c=getchar();}
    return ans*f;
}
int n,m,k,f[M],h[M],usd[M];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
int first[M],cnt,c[M];
struct node{int to,next;}e[2*M];
void ins(int a,int b){
    cnt++; e[cnt].to=b; e[cnt].next=first[a]; first[a]=cnt;
}
void insert(int a,int b){ins(a,b); ins(b,a);}
int main()
{
    int x,y;
    n=read(); m=read();
    for(int i=1;i<=n;i++) f[i]=i;
    for(int i=1;i<=m;i++) x=read(),y=read(),insert(x,y);
    for(int i=1;i<=n;i++) c[i]=read();
    for(int i=n;i>=1;i--){
        k++; usd[c[i]]=1;
        int now=c[i],p=find(now);
        for(int j=first[now];j;j=e[j].next){
            if(!usd[e[j].to]) continue;
            int q=find(e[j].to);
            if(p==q) continue;
            f[q]=p; k--; 
        }
        h[i]=k;
    }
    for(int i=1;i<=n;i++)
        if(h[i]==1) printf("YES\n");
        else printf("NO\n");
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/lyzuikeai/p/7278732.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值