Gym 100342I Travel Agency (Tarjan)

题意读懂了就好做了,就是求一下点双连通分量。维护一下一颗子树的结点数,对于一个结点当u是割点的时候,

统计一下u分割的连通分量v,每得到一个连通分量的结点数cnt(v)和之前连通分量结点数sum相乘一下就好。最后加一下和u的子树上的连通分量总数和其它的结点的乘积。

B,C中其中一者可以是A,所有最后还要加上n-1。

补下以前的题

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int maxn = 20005;
vector<int> G[maxn];
#define PB push_back

int dfn[maxn],low[maxn],dfs_clock,ans[maxn],cnt[maxn];

void tarjan(int u,int fa = -1)
{
    dfn[u] = low[u] = ++dfs_clock;
    cnt[u] = 1; ans[u] = 0;
    int sum = 0;
    for(int i = 0; i < G[u].size(); i++){
        int v = G[u][i];
        if(!dfn[v]){
            tarjan(v,fa);
            if(low[v]>=dfn[u]){
                ans[u] += sum*cnt[v];
                sum += cnt[v];
            }
            low[u] = min(low[v],low[u]);
            cnt[u] += cnt[v];
        }else if(v != fa && dfn[v] < dfn[u] ) { low[u] = min(low[u],dfn[v]); }
    }
    ans[u] += (n-sum-1)*sum;

}

int main()
{
    freopen("travel.in","r",stdin);
    freopen("travel.out","w",stdout);
    cin>>n>>m;
    for(int i = 0; i < m; i++){
        int u,v; scanf("%d%d",&u,&v);
        G[u].PB(v); G[v].PB(u);
    }
    tarjan(1);
    for(int i = 1; i <= n; i++) printf("%d
",ans[i]+n-1);
    return 0;
}
原文地址:https://www.cnblogs.com/jerryRey/p/4770915.html