题目链接:https://codeforces.com/problemset/problem/1454/E
题目其实很容易,一开始有点逃避了…
很容易知道n个点,n条边的连通图必有环,如果我们不断的把度数为1的点去掉,直到没有点可以再去除的时候,剩下的点就是在环上的点。
因为是树加一条边,所以直接进行遍历操作的复杂度为O(n),完全不担心时间上的问题。
接下来就是分类讨论了。
1、对于两个点都在环上,那么两点之间有两条路径
2、一个点在环上,一个点在环外,若环外点是环上点的分支,则只有一条路径,否则两条路径
3、环外点与环外点,对于在同一条分支上的点,只有一条路径,在不同分支上则有两条。
因此进行DFS,对每个环上点遍历记录其对应分支上有多少个结点即可。
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=2e5+5;
typedef long long ll;
vector<int>e[maxn];
bool vis[maxn];
int du[maxn],ans[maxn],num[maxn];
int dfs(int x,int fa){
int res=0;
for(int i=0;i<e[x].size();i++){
if(!vis[e[x][i]]||e[x][i]==fa)continue;
res+=dfs(e[x][i],x); res++;
}
return res;
}
int main(void){
//freopen("in.txt","r",stdin);
int t;
scanf("%d",&t);
while (t--)
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++){
e[i].clear();
vis[i]=du[i]=0;
}
for(int i=1;i<=n;i++){
int u,v;scanf("%d %d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
du[u]++;du[v]++;
}
queue<int>q;
for(int i=1;i<=n;i++)if(du[i]==1)q.push(i);
while(!q.empty()){
int u=q.front(); q.pop();
vis[u]=1;
for(int i=0;i<e[u].size();i++){
du[e[u][i]]--;
if(du[e[u][i]]==1)q.push(e[u][i]);
}
}
int tot=0;
for(int i=1;i<=n;i++){
if(!vis[i]){
ans[++tot]=i;
num[tot]=dfs(i,-1);
}
}
ll res=1LL*tot*(tot-1);
for(int i=1;i<=tot;i++){
res+=n-tot-num[i];
res+=n-tot;
res+=1LL*num[i]*(num[i]-1)/2+1LL*num[i]*(n-tot-num[i]);
}
printf("%lld\n",res);
}
return 0;
}