传送门:
Codeforces Round #686 (Div. 3)
题意分析:给定一个n顶点n条边的联通图,求图中简单路径的数量。
n顶点的树加了条边,必定会出现唯一环。经查资料这是一棵基环树。
如果两个顶点的路径经过环内边,那么这两个顶点之间有2条路径,否则为1条。
应该是可以用树形dp求出来的,我太菜了,不会。
从反面做比较方便,先假设每一个顶点都在环内,在减去不在环内点减少的路径树。
首先找到环,从环内每个顶点向外拓展,计算每个顶点子树的大小,减去相应的路径数量即为图中简单路径的数量。
ac代码:
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<string.h>
#include<string>
#include<math.h>
#define INF 0x7fffffff
#define IOS; {ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);}
#define debug(x) {cout<<"<<<<"<<(x)<<endl;}
const int N = 200005;
typedef long long ll;
using namespace std;
vector<int>g[N];
int fa[N];
int dfn[N];
bool vis[N];
int id;
int n;
void find_loop(int u)//找环
{
dfn[u]=++id;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(dfn[v])
{
if(dfn[v]<dfn[u])
continue;
while(v!=fa[u])
{
vis[v]=1;
v=fa[v];
}
return;
}
fa[v]=u;
find_loop(v);
}
}
ll dfs(ll u)
{
vis[u]=1;
ll ans=1;
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(vis[v])continue;
ans+=dfs(v);
}
return ans;
}
void init()
{
id=0;
for(int i=1;i<=n+1;i++)
g[i].clear();
memset(dfn,0,sizeof(int)*(n+5));
memset(vis,0,n+5);
}
int main() {
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
IOS;
int t;cin>>t;
while(t--)
{
cin>>n;
init();
for(int i=1;i<=n;i++)
{
int u,v;cin>>u>>v;
g[u].push_back(v);
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 t=dfs(i);
ans-=t*(t-1)/2;
}
}
cout<<ans<<endl;
}
return 0;
}