CF1454 E. Number of Simple Paths (基环树 拓扑排序)

原题链接
题意:
给定一个n个点n条边的树,求树上简单路径的数量。
思路:
N个点N条边的连通无向图,即在树上加一条边恰好包含一个环的图,称为基环树。
树上两点的路径是唯一确定的。
拓扑排序可以用来判断有向图是否有环。
代码:

#pragma GCC optimize(3)
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll>PLL;
typedef pair<int,int>PII;
typedef pair<double,double>PDD;
#define I_int ll
inline ll read()
{
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
char F[200];
inline void out(I_int x)
{
    if (x == 0) return (void) (putchar('0'));
    I_int tmp = x > 0 ? x : -x;
    if (x < 0) putchar('-');
    int cnt = 0;
    while (tmp > 0)
    {
        F[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (cnt > 0) putchar(F[--cnt]);
    //cout<<" ";
}
ll ksm(ll a,ll b,ll p)
{
    ll res=1;
    while(b)
    {
        if(b&1)res=res*a%p;
        a=a*a%p;
        b>>=1;
    }
    return res;
}
const int inf=0x3f3f3f3f,mod=1e9+7;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int maxn=2e5+7,N=1e7+10;
const double PI = atan(1.0)*4;
const double eps=1e-6;
int h[maxn],idx;
struct node
{
    int e,ne;
} edge[maxn*2];
int din[maxn];
void add(int u,int v) ///存储边
{
    edge[idx]= {v,h[u]};
    h[u]=idx++;
}
queue<int>q;
bool st[maxn];
void topsort() ///变形拓扑排序求无向图的环
{
    while(!q.empty())
    {
        int u=q.front();
        q.pop();
        st[u]=1;
        for(int i=h[u]; ~i; i=edge[i].ne)
        {
            int j=edge[i].e;
            if(--din[j]==1) q.push(j);
        }
    }
}
int dfs(int u,int fa) ///求子树大小
{
    int res=1;
    for(int i=h[u]; ~i; i=edge[i].ne)
    {
        int j=edge[i].e;
        if(j==fa||!st[j]) continue;
        res+=dfs(j,u);
    }
    return res;
}
void init()///初始化
{
    memset(h,-1,sizeof h);
    memset(st,0,sizeof st);
    memset(din,0,sizeof din);
    idx=0;
    while(!q.empty()) q.pop();
}
int main()
{
    int t=read();
    while(t--)
    {
        init();
        int n=read();
        for(int i=1; i<=n; i++)
        {
            int u=read(),v=read();
            add(u,v);
            add(v,u);
            din[u]++;
            din[v]++;
        }
        ///求环
        for(int i=1; i<=n; i++)
            if(din[i]==1) q.push(i);
        topsort();
        ll res=1ll*n*(n-1);
        for(int i=1; i<=n; i++)
            if(!st[i])
            {
                ll tmp=dfs(i,i);
                res=res-(tmp-1)*tmp/2;
            }
        printf("%lld\n",res);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆沙睡不醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值