题意:
给一个 n 个点 n 条边 的联通图,求图中的路径数 (1 - 2 和 2 - 1算同一条路径)。
思路:
- 由于是 n 个点 n 条边,所以就是一棵树加了一条边,必定存在一个环,所以我们只需要找到这个环上的点,求出每个点有多少个儿子节点(每个点 dfs 一遍就好了),然后就是计数,如果两个点都在环上,或者属于两个不同的子树,那么两点间的路径数就是 2 ,如果属于同一子树路径数就是 1 .
- 可以用 topo 找环,每次把度为 1 的点去除,最后留下的点就是环上的点。
代码:
#include<iostream>
#include<cstdio>
#include<map>
#include<math.h>
#include<queue>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 7;
int head[maxn],du[maxn],num,n,T;
int vis[maxn];
ll ans = 0,sum,siz[maxn];
vector< int >vec;
struct node{
int to,next;
}e[maxn<<1];
void add(int u,int v){
e[num].to = v;
e[num].next = head[u];
head[u] = num ++;
}
void topo(){
queue< int > que;
for(int i = 1; i <= n ; i ++){
if(du[i] == 1){
que.push(i);
}
}
while(que.size()){
int u = que.front();
que.pop();
for(int i = head[u]; i != -1 ; i = e[i].next){
int to = e[i].to;
du[to] -- ;
if(du[to] == 1){
que.push(to);
}
}
}
}
void dfs(int u,int pre){
siz[u] = 1;
for(int i = head[u]; i != -1 ; i = e[i].next){
int v = e[i].to;
if(v == pre || vis[v]) continue;
dfs(v , u);
siz[u] += siz[v];
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
vec.clear();
ans = sum = 0;
num = 0;
for(int i = 1; i <= n ; i ++){
head[i] = -1;
du[i] = siz[i] = vis[i] = 0;
}
for(int i = 1,u,v; i <= n ; i ++){
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
du[u]++,du[v]++;
}
topo();
for(int i = 1; i <= n; i ++){
if(du[i] >= 2){
vec.push_back(i);
vis[i] = 1;
}
}
for(int i = 1; i <= n; i ++){
if(du[i] >= 2){
dfs(i , -1);
}
}
ll temp = 0;
for(auto u : vec){
ans += siz[u] * (siz[u] - 1) / 2;
ans += siz[u] * temp * 2;
temp += siz[u];
}
printf ("%lld\n",ans);
}
return 0;
}