题目传送门
题意:有n个点、m条边的无向图,对任意1≤i≤n,求出将第i个点和与其关联的边删去时,有多少个有序点对(u,v)满足u和v不连通。
显然,这道题需要用到割点。
先考虑i不是割点的情况。因为i不是割点,所以把i和与其相关联的边去掉时,其他n−1个点依然是连通的。因此答案为2(n−1)。(注意题目中说的是有序点对)
然后来考虑i是割点的情况。
很显然,若i是割点,删去i和与其相关联的边后,原图会变成若干个连通块。那怎么求这些连通块的大小呢?
我们思考一下:tarjan算法是如何判定割点的?
答案是:在搜索树上,如果i不是根节点,则如果有任意soni满足dfn[i]≤low[soni],i即为割点。当dfn[i]≤low[soni]时,soni除了往上走树边到达i以外不能到达i子树外的地方,也就是说删除(i,soni)这条边后,图会分为soni的子树与其它这两个部分。所以i就是割点。
所以这道题的答案就很显而易见了:对任意满足割点判定式dfn[i]≤low[soni]的儿子soni,在删去i和与其相关联的边后,soni的子树会变成图中的一个单独的连通块。因此我们只需要在tarjan找割点的同时统计一下这些子树的大小加进答案里即可。
别忘了还有一个连通块是除了i子树以外的所有点。
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <bits/stdc++.h> #define int long long #define rep(i,a,b) for(int i=(a);i<=(b);++i) #define per(i,a,b) for(int i=(a);i>=(b);--i) using namespace std; const int N=1e5+5,M=1e6+5; int target[M],last[N],pre[M],tot=0; int n,m; int ans[N]; int dfn[N],low[N],sz[N],cnt=0; void add(int u,int v) { target[++tot]=v; pre[tot]=last[u]; last[u]=tot; } int tarjan(int u){ sz[u]=1; dfn[u]=low[u]=++cnt; int flg=0,sum=0; bool iscut=false; for(int ptr=last[u];ptr;ptr=pre[ptr]) { int v=target[ptr]; if(!dfn[v]) { sz[u]+=tarjan(v); low[u]=min(low[u],low[v]); if(low[v]>=dfn[u]) { ++flg; ans[u]+=sz[v]*(n-sz[v]); sum+=sz[v]; if(u!=1||flg>1) iscut=true; } } else low[u]=min(low[u],dfn[v]); } if(iscut) ans[u]+=(sum+1)*(n-sum-1)+(n-1); else ans[u]=2*(n-1); return sz[u]; } signed main(){ scanf("%lld%lld",&n,&m); rep(i,1,m) { int u,v; scanf("%lld%lld",&u,&v); add(u,v);add(v,u); } tarjan(1); rep(i,1,n) printf("%lld\n",ans[i]); return 0; }
|