「APIO2018」铁人两项-圆方树

Description

给定一张无向图,询问存在多少三元组 ( s , c , f ) (s,c,f) s , c , f s,c,f 各不相同)满足存在一条从 s s c c c c f f 的简单路径。

n 1 0 5 , m 1 0 5 n \leq 10^5, m \leq 10^5

Solution

考虑建立圆方树。

固定 s , f s,f ,满足条件的 c c 一定存在 s s f f 路径上方点所代表的点双上。

不妨设圆点权值为 1 -1 ,方点权值为其所代表的点双的大小。那么满足条件的点 c c 数量为路径上的权值(路径上的圆点为割点,存在于多个点双上,正好抵消)。

所以答案为经过某个点的路径条数乘其权值的和。树形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;
}

猜你喜欢

转载自blog.csdn.net/DSL_HN_2002/article/details/89499117