题意:T组测试:每组测试给一图, n和节点n条边且是连通图;问有多少种长度大于等于1的简单路径。
解题思路:
根据已知信息可知:
1.图是一个n个节点n条边的连通图, 那么图中必然有一个环~~(loop)~~ ;
2.长度大于等于1的简单路径为任意两点间的路径;
根据这两个信息可推出:
①.在环中的任意两点皆有两条路径;
②.以环为根节点的左子树上的点与右子树上的点之间也是有两条路径;
③.以环为根节点左子树中任意两点只有一条路径,右子树一样;
那么,若n个节点n条边的连通图任意两点的路径条数都为2 则有 sum = n * (n - 1) 条,用 sum - ③中得到的路径数,
即为所求。
代码:
#include <iostream>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
typedef long long LL;
const int maxn = 2e5 + 7, maxm = 4e5 + 7;
int h[maxn], e[maxm], ne[maxm], cnt;
int d[maxn], vis[maxn], jud[maxn];
LL s[maxn];
void add(int u, int v){
e[cnt] = v;
ne[cnt] = h[u];
h[u] = cnt ++;
}
vector<int> loop; // 存储环中的节点
int n;
void topsort(){ // 桶过拓扑排序的方法 可以得到环中的结点
memset(vis, 0, sizeof vis);
queue<int> q;
for (int i = 1; i <= n; i ++ ) if (d[i] == 1) q.push(i);
while(!q.empty()){
int u = q.front(); q.pop();
vis[u] = 1;
for (int i = h[u]; ~i; i = ne[i]){
int v = e[i];
d[v] --;
if (d[v] == 1) q.push(v);
}
}
for (int i = 1; i <= n; i ++ ) if (vis[i] == 0) loop.push_back(i), jud[i] = 1; // 将换中的节点 压入栈, 并标记一下
}
void dfs(int u, int f){ // 以环中有向外连接的结点为根 记录 子树大小
// s[u] = 1;
for (int i = h[u]; ~i; i = ne[i]){
int v = e[i];
if (jud[v] || v == f) continue; //
dfs(v, u);
s[f] += s[v];
}
}
int main (){
int T;
cin >> T;
while (T -- ){
cin >> n;
// 初始化 莫忘记
for (int i = 0; i<= n; i ++ ) h[i] = -1, d[i] = 0, jud[i] = 0;
cnt = 0;
loop.clear();
for (int i = 0; i<= n; i ++ ) s[i] = 1;
for (int i = 0, u, v; i < n; i ++ ){
scanf ("%d%d", &u, &v);
add(u, v);
add(v, u);
d[u] ++, d[v] ++;
}
topsort();
LL ans = 1ll * n * (n - 1);
for (int i = 0; i < loop.size(); i ++ ){
int u = loop[i];
dfs(u, u);
ans -= (s[u] * (s[u] - 1) / 2ll);
}
printf ("%lld\n", ans);
}
}