题意
给一个n个点n条边的无向图(基环树特点嘤嘤嘤),找出有多少不同的简单路径。。。什么是简单路径,就是连接两个点的一条通路,但是如果只有方向不同,那么两条被视为一条简单路径。。
思路
分类讨论:
1.如果两个点都在环上,顺时针可以,逆时针也可以,ans = ans + 2;
2.如果一个点在环上,一个不在环上,而且不在同一棵树上,两条路径,经过环的时候顺时针走,逆时针走都可以,所以,ans = ans + 2;
3.如果在同一棵树上,那就只有一条,ans = ans + 1;
然后我们可以发现,只有在同一棵树上的时候才是ans + 1,那么我们就可以想到用 总的路径数(n * (n - 1)) - 所有的树的在同一棵树上的任意两个点之间的路径数(tot * (tot - 1)/ 2)的和
AC代码:
#include<cstdio>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
vector<int>G[maxn];
int fa[maxn];
int vis[maxn];
int dfn[maxn];
int cnt = 0;
void find_loop(int now){//找环
dfn[now] = ++cnt;
for(int i = 0;i < G[now].size();i++){
int to = G[now][i];
if(to == fa[now]) continue;
if(dfn[to]){
if(dfn[to] < dfn[now]) continue;//???什么意思
while(to != fa[now]){
vis[to] = 1;
to = fa[to];
}
return ;
}
fa[to] = now;
find_loop(to);
}
}
int dfs(int now){//寻找子树的结点的个数
int num = 1;
vis[now] = 1;
for(int i = 0;i < G[now].size();i++){
int to = G[now][i];
if(vis[to]) continue;
num += dfs(to);
}
return num;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i++) G[i].clear();
memset(fa,0,sizeof(fa));//存当前结点的父亲
memset(vis,0,sizeof(vis));//有没有遇到过
memset(dfn,0,sizeof(dfn));//访问的时间
int u,v;
for(int i = 1;i <= n;i++){
scanf("%d%d",&u,&v);
G[u].push_back(v);//vector存图
G[v].push_back(u);
}
find_loop(1);//找环
ll ans = 1ll * n * (n - 1);//全部的路径数
for(int i = 1;i <= n;i++){
if(vis[i]){
ll tmp = dfs(i);//统计以i为根的子树的结点个数
ans = ans - tmp * (tmp - 1) / 2;//减掉
}
}
printf("%lld\n",ans);
}
return 0;
}