Description
给定一张无向图,询问存在多少三元组 ( 各不相同)满足存在一条从 到 , 到 的简单路径。
Solution
考虑建立圆方树。
固定 ,满足条件的 一定存在 到 路径上方点所代表的点双上。
不妨设圆点权值为 ,方点权值为其所代表的点双的大小。那么满足条件的点 数量为路径上的权值(路径上的圆点为割点,存在于多个点双上,正好抵消)。
所以答案为经过某个点的路径条数乘其权值的和。树形DP即可。
#include <bits/stdc++.h>
using namespace std;
inline int gi()
{
char c = getchar();
while(c < '0' || c > '9') c = getchar();
int sum = 0;
while('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
return sum;
}
typedef long long ll;
const int maxn = 100005;
int n, m;
struct edge
{
int to, next;
} e[maxn * 4];
int h[maxn], tot;
inline void add(int u, int v)
{
e[++tot] = (edge) {v, h[u]}; h[u] = tot;
e[++tot] = (edge) {u, h[v]}; h[v] = tot;
}
int dfn[maxn], low[maxn], Time, stk[maxn], top;
vector<int> to[maxn << 1];
int cnt, vis[maxn << 1], val[maxn << 1];
ll up[maxn << 1], down[maxn << 1], ans;
void tarjan(int u, int fa)
{
dfn[u] = low[u] = ++Time;
stk[++top] = u;
for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
if (!dfn[v]) {
tarjan(v, u);
low[u] = min(low[u], low[v]);
if (dfn[u] == low[v]) {
++cnt;
while (stk[top + 1] != v)
to[stk[top]].push_back(cnt), to[cnt].push_back(stk[top]), --top, ++val[cnt];
to[u].push_back(cnt), to[cnt].push_back(u); ++val[cnt];
}
} else low[u] = min(low[u], dfn[v]);
}
void dfs1(int u, int fa)
{
vis[u] = 1;
if (u <= n) down[u] = 1;
for (int v : to[u])
if (v != fa) {
dfs1(v, u);
down[u] = down[u] + down[v];
}
}
void dfs2(int u, int fa)
{
vis[u] = 2;
ll Ans = 0;
if (u <= n) ans += val[u] * up[u], Ans = up[u], ++up[u];
ll sum = up[u];
for (int v : to[u])
if (v != fa) {
up[v] = up[u] + down[u] - down[v] - (u <= n);
ans += sum * down[v] * val[u]; Ans += sum * down[v]; sum += down[v];
dfs2(v, u);
}
//printf("%d %lld\n", u, Ans);
}
int main()
{
n = gi(); m = gi();
for (int i = 1; i <= m; ++i) add(gi(), gi());
cnt = n;
for (int i = 1; i <= n; ++i)
if (!dfn[i]) tarjan(i, 0), --top;
fill(val + 1, val + n + 1, -1);
for (int i = 1; i <= n; ++i) if (!vis[i]) dfs1(i, 0);
for (int i = 1; i <= n; ++i) if (vis[i] == 1) dfs2(i, 0);
printf("%lld\n", ans * 2);
return 0;
}