Luogu

LOJ

分析

可以发现 $s$ 到 $t$ 的所有简单路径的并相当于 $s$ 到 $t$ 经过的所有点双连通分量(这里一条边也算点双)的并。

那么对于一对 $(s,t)$,满足条件的 $c$ 的数量就等于 $s$ 到 $t$ 经过的所有点双的大小之和减 $2$。

考虑怎么快速求两点之间所有点双大小之和。容易想到广义圆方树,可以想到将方点的权值设成点双大小,圆点的权值设成 $-1$,这样子 $s$ 到 $t$ 路径上的权值和就是对于 $(s,t)$ 来说满足条件的 $c$ 的数量了。

那么现在只需要统计每两个圆点路径上权值和的和,可以考虑每个点作为 $c$ 的贡献并 DFS 求出。

代码

// ====================================
//   author: M_sea
//   website: https://m-sea-blog.com/
// ====================================
#include <bits/stdc++.h>
#define file(x) freopen(#x".in","r",stdin); freopen(#x".out","w",stdout)
using namespace std;
typedef long long ll;

int read() {
    int X=0,w=1; char c=getchar();
    while (c<'0'||c>'9') { if (c=='-') w=-1; c=getchar(); }
    while (c>='0'&&c<='9') X=X*10+c-'0',c=getchar();
    return X*w;
}

const int N=300000+10;

int n,m,tot,sumsz,w[N];
vector<int> E[N],T[N];

int dfn[N],low[N],tim=0,sta[N],top=0;
void tarjan(int u) {
    dfn[u]=low[u]=++tim,sta[++top]=u; ++sumsz,w[u]=-1;
    for (int v:E[u]) {
        if (!dfn[v]) {
            tarjan(v),low[u]=min(low[u],low[v]);
            if (low[v]>=dfn[u]) {
                ++tot; w[tot]=1;
                T[tot].emplace_back(u),T[u].emplace_back(tot);
                while (1) {
                    int t=sta[top--]; ++w[tot];
                    T[tot].emplace_back(t),T[t].emplace_back(tot);
                    if (t==v) break;
                }
            }
        }
        else low[u]=min(low[u],dfn[v]);
    }
}

int sz[N]; ll ans=0;
void dfs(int u,int fa) {
    sz[u]=u<=n;
    for (int v:T[u]) {
        if (v==fa) continue;
        dfs(v,u);
        ans+=2ll*sz[u]*sz[v]*w[u];
        sz[u]+=sz[v];
    }
    ans+=2ll*sz[u]*(sumsz-sz[u])*w[u];
}

int main() {
    n=read(),m=read();
    for (int i=1;i<=m;++i) {
        int u=read(),v=read();
        E[u].emplace_back(v),E[v].emplace_back(u);
    }
    tot=n;
    for (int i=1;i<=n;++i)
        if (!dfn[i]) sumsz=0,tarjan(i),dfs(i,0);
    printf("%lld\n",ans);
    return 0;
}
最后修改:2020 年 08 月 12 日 04 : 22 PM