New Reform [CodeForces-659E]

题意

有n个点和m条不确定方向的边。
现在要求确定这m条不确定方向的边,使得入度为0的点的个数最少。
输出最小的个数。

分析

如果某个点保证入度>0
那么可以把其它所有连向的点的入度都>0
n,m>=10^5不算特别大,感觉要么O(n)要么O(n log n)
可以一个一个联通块,考虑某一条边的方向,然后跑两次找较小值然后加入答案。
不过方案不能确定。
如果联通块是一棵树那么res++ 否则不产生改变。
其实想到上面这些应该就差不多了。(虽然复杂度我好像没猜对)

其实就是判断一个联通块是树还是存在环。
这里通过并查集来实现,如果合并一条边的时候两个点已经在同一个联通块内的话,那么连接它们两个一定会在这个联通块内产生环。同时也可以发现只有这个情况才会存在环。

注意在合并两个联通块的时候,如果存在一个联通块内有环,那么新的联通块内也有环。
结束。

如何证明如果存在树的话一定有一个点入度为0,而存在简单环,就可以使得块内所有的点入度都不为0。

如果是树的话,可以发现选择任意一个点做根节点,除了跟节点以外所有的点入度都不为0。同时不存在方案使得所有的点入度都不为0。
如果是环的话,那么让环上的点首尾相接,环上所有的点入度都不为0。然后存在一些分叉的话,就让环上的点作为树根,然后就可以做到联通块内所有的点入度都不为0。

算法

并查集
(跑得快而且优雅(???)用来判环也十分方便)

code

#include<bits/stdc++.h>
using namespace std;
void read(int &x){
    x=0; char c=getchar();
    for (;c<48;c=getchar());
    for (;c>47;c=getchar())x=(x<<1)+(x<<3)+(c^48);
}
#define M 100005
int fa[M];
bool flag[M];
int get(int x){
    if (fa[x]==x)return x;
    return fa[x]=get(fa[x]);
}
int main(){
//  freopen("1.in","r",stdin);
    int n,m,x,y,res=0,fx,fy;
    read(n); read(m);
    for (int i=1;i<=n;i++)fa[i]=i;
    for (int i=1;i<=m;i++){
        read(x); read(y);
        fx=get(x); fy=get(y);
        if (fx==fy){
            flag[fx]=1;
        }
        else{
            fa[fx]=fy;
            flag[fy]|=flag[fx];
        }
    }
    for (int i=1;i<=n;i++){
        fx=get(i);
        if (!flag[fx]){
            res++;
            flag[fx]=1;
        }
    }
    printf("%d\n",res);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值