https://ac.nowcoder.com/acm/contest/7501/D
思路:用Tarjan算法的思路来写,dfn[i]代表结点 i 的dfs序,low[i]代表从结点 i dfs下去能到达的dfn值最小的结点的dfn值。
若x为当前结点,y为一个x的未被访问过的子节点,若对 y dfs完毕后,得到 low[y] >= dfn[x]则说明从x的父亲结点,经过x能够到达y,如果把结点x删掉,则不能到达结点y了。
这就相当于删去结点x时,x的父亲结点与结点y会被分成两个连通块,即这两部分无法相互到达,因此只需要数一下对于结点x,它有多少个儿子结点y,满足low[y]>=dfn[x],当然其实x的父亲结点也算是一个儿子节点,因此需要+1表示算上父亲节点。但每次dfs的起点是没有父亲节点的,所以不需要+1.
综上所述,删去一个结点x会造成怎样的影响呢?假设删去结点x会增加ans[x]个连通块,这个无向图初始包含m个连通块,这个节点原来是属于1个连通块的,它删去后会增加ans[x]个连通块,那么删去这个结点后连通块的数量应为m-1+ans[x]。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=3e5+10; int head[N],ver[N<<1],Next[N<<1]; int dfn[N],low[N],ans[N]; int n,m,tot,root,num=0,sum=0; void add(int x,int y){ ver[++tot]=y,Next[tot]=head[x],head[x]=tot; } void tarjan(int x){ dfn[x]=low[x]=++num; ans[x]=(x!=root); for(int i=head[x];i;i=Next[i]){ int y=ver[i]; if(!dfn[y]){ tarjan(y); low[x]=min(low[x],low[y]); ans[x]+=(low[y]>=dfn[x]); } else low[x]=min(low[x],dfn[y]); } } int main(){ scanf("%d%d",&n,&m); tot=0; for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); if(x==y) continue; add(x,y); add(y,x); } for(int i=1;i<=n;i++) if(!dfn[i]) root=i,tarjan(i),sum++; for(int i=1;i<=n;i++) printf("%d%c",sum+ans[i]-1,i==n?'\n':' '); return 0; }