小K的技术
小K的技术
考虑每个弱连通分量(即无视边的方向形成的各个连通分量,内部点彼此可达),答案是独立的。
而且对于一个弱连通分量,若不存在环,我们可以发现其实只需要点数 \(k-1\) 的边数即可,因为原图是 DAG
,所以可以让这个类似于一棵树。
而如果存在环,说明依赖关系并不是顺序结构,那么需要多一条边,而且根据一个环一定满足,那么最多也就 \(k\) 条边。
于是,我们对于每个弱连通分量判断是否存在环,如果不存在,答案就减去一。
关于环的判断,可以考虑黑白灰染色。对于每个点,未遍历时标记为 \(0\),第一次遍历时标记为 \(1\),表明它的后继节点没有遍历完成,往后遍历若遇到 \(0\) 则过去遍历,否则如果遇到 \(1\) 标记说明存在环。
#include<cstdio>
#include<cstring>
#define sh short
using namespace std;
#define Ed for(int i=h[x];~i;i=ne[i])
#define Ls(i,l,r) for(int i=l;i<r;++i)
#define Rs(i,l,r) for(int i=l;i>r;--i)
#define Le(i,l,r) for(int i=l;i<=r;++i)
#define Re(i,l,r) for(int i=l;i>=r;--i)
#define L(i,l) for(int i=0;i<l;++i)
#define E(i,l) for(int i=1;i<=l;++i)
#define W(t) while(t--)
#define Wh while
const int N=100010,M=2*N;
int n,m,ans,nidx;
int h[N],e[M],ne[M],idx,st[N];//don't forget memset h!
bool cir[N];
sh vis[N];
void add(int a,int b){
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void dfs(int x){
st[x]=nidx;
Ed{
int j=e[i];
if(!st[j])dfs(j);
}
}
bool Dfs(int x){
vis[x]=1;
Ed{
if(i&1)continue;
int j=e[i];
if(!vis[j]&&Dfs(j))return 1;
else if(vis[j]==1)return 1;
}
vis[x]=2;
return 0;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("1.in","r",stdin);
#endif
scanf("%d%d",&n,&m);
memset(h,-1,n*4+4);
E(i, m){
int a,b;
scanf("%d%d",&a,&b);
add(a,b);
add(b,a);
}
int tot=n;
E(i, n)
if(!st[i])++nidx,dfs(i);
E(i, n){
if(cir[st[i]])continue;
if(!vis[i])cir[st[i]]=Dfs(i);
}
E(i, nidx)tot-=!cir[i];
printf("%d",tot);
return 0;
}