E

一个图,n个点,n条边,没有重边和自环。

那么多出的一条边必定使他成为基环树。

要求去计算简单路径的个数。

简单路径:与方向无关的路径。

又因为在树上,两点的路径唯一确定,那么路径仅仅与起点与终点有关。

也就是C(2,n)这样。

但是如果路径经过环,那么中间经过环的部分就可以有两种走法。

也就是说答案=走过环的路径+没有走过环的路径

假设所有路径都过环,再减去树内的路径

通过拓扑排序和并查集的方式,将这个基环树划分为几个子树。

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define MAX 1000001
#define inf 0x3f3f3f3f
#define ll long long
#define MAX 1000001
const ll N = 2e5+7;
const ll mod = 1e9+7;
using namespace std;
ll deg[N],cnt,q[N],fa[N],num[N],n;
vector<int> G[N];
int find(int u){
    return fa[u]==u?u:fa[u]=find(fa[u]);
}
void unite(int u,int v){
    u=find(u),v=find(v);
    if(u==v)    return ;
    fa[u]=v;
    num[v]+=num[u];
}
int main(){
    int t;scanf("%d",&t);
    while(t--){
        scanf("%lld",&n);
        //初始化 
        cnt=0;
        for(int i=1;i<=n;++i){
            G[i].clear();
            num[i]=1;fa[i]=i;deg[i]=0;
        }
        
        for(int i=1;i<=n;++i){
            int u,v;
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
            G[v].push_back(u);
            deg[u]++;deg[v]++;
        }
        
        //找根 
        for(int i=1;i<=n;++i)
            if(deg[i]==1)    q[++cnt]=i;
        //划分出各个基环上的树 
        for(int i=1;i<=cnt;++i){
            int u=q[i];
            for(int j=0;j<G[u].size();++j){
                int v=G[u][j];
                unite(u,v); 
                deg[v]--;
                if(deg[v]==1)    q[++cnt]=v;
            }
        }
        
        ll ans=n*(n-1);
        for(int i=1;i<=n;++i){
            if(i==fa[i]){
                ans=ans-(1ll*num[i]*(num[i]-1)/2);
            }
        }
        printf("%lld
",ans);
    }
    return 0; 
}
原文地址:https://www.cnblogs.com/PdrEam/p/14526881.html