题意:
给定n个点n条边的联通无向图,问图中有多少种简单路径.
数据范围:n<=2e5
解法:
显然给定的图是基环树.
如果给定的图是一棵普通的树,那么答案就是n*(n-1)/2.
求出环,然后以环为根,分类讨论:
1.环上的点到环上的点,每对点之间有两种路径.
2.环外的点到环外的点,如果两点不在同一颗子树,那么有两种路径,否则一种.
3.环上的点到环外的点,如果是去自己子树中的点,那么一种,否则两种.
发现只需要在基环树上树形dp求一下sz[]就行了.
求环可以用拓扑排序,不断删掉度数为1的点,最后剩下的点就是环上的点了.
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
vector<int>g[maxm];
int sz[maxm];
int d[maxm];
int n;
void dfs(int x,int fa){
sz[x]=1;
for(int v:g[x]){
if(v==fa||d[v]!=1)continue;
dfs(v,x);
sz[x]+=sz[v];
}
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++){
g[i].clear();
d[i]=0;
}
for(int i=1;i<=n;i++){
int a,b;cin>>a>>b;
g[a].push_back(b);
g[b].push_back(a);
d[a]++,d[b]++;
}
queue<int>q;
for(int i=1;i<=n;i++){
if(d[i]==1){
q.push(i);
}
}
while(!q.empty()){
int x=q.front();q.pop();
for(int v:g[x]){
if(d[v]>1){
d[v]--;
if(d[v]==1){
q.push(v);
}
}
}
}
int cir=0;
for(int i=1;i<=n;i++){
if(d[i]!=1){
cir++;
dfs(i,-1);
}
}
int ans=0;
for(int i=1;i<=n;i++){
if(d[i]!=1){
//环上到环上
ans+=(cir-1);//到环上其他点
//环上到环外
ans+=(sz[i]-1);//到自己子树
ans+=(n-cir-(sz[i]-1))*2;//到其他子树
//环外到环外
ans+=(sz[i]-1)*(sz[i]-1-1)/2;//子树到子树其他点
ans+=(sz[i]-1)*(n-cir-(sz[i]-1));//子树到其他子树
}
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(0);cin.tie(0);
int TT;cin>>TT;
while(TT--){
solve();
}
return 0;
}