LOJ#2587[APIO2018]Duathlon铁人两项(圆方树)

题目链接

洛谷

LOJ

前置知识

圆方树

解析

考虑一条从\(s\)\(f\)的路径产生的贡献是除\(s, f\)外经过的点数

如果这条路径经过了某个点双连通分量,点双上的每个点都会产生贡献

点双\(\rightarrow\)圆方树

建出圆方树,方点权值为代表的点双的大小

这样两点间的路径对应成两个圆点间的路径,由于一条路径会经过\(s\)\(f\)自己所在的点双,而\(c\)不能在\(s\)\(f\),所以每个圆点权值为\(-1\)

于是一对\(s, f\)的贡献就是圆方树上的路径上的点权和

考虑每个点被多少条路径经过,乘上点权就是这个点对答案的贡献

于是就变成了一个简单的树上路径统计问题

因为交换\(s\)\(f\)算不同的方案,最后答案乘\(2\)

代码

自带大常数……洛谷\(1000ms\)\(LOJ \ 5600ms\)……

#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define MAXN 100005
#define MAXM 200005

typedef long long LL;
struct Graph {
	struct Edge {
		int v, next;
		Edge(int _v = 0, int _n = 0):v(_v), next(_n) {}
	} edge[MAXM << 1];
	int head[MAXN << 1], cnt;
	void init() { memset(head, -1, sizeof head); cnt = 0; }
	void add_edge(int u, int v) { edge[cnt] = Edge(v, head[u]); head[u] = cnt++; }
	void insert(int u, int v) { add_edge(u, v); add_edge(v, u); }
};

void Tarjan(int, int);
void dfs(int, int);

int N, M, dfn[MAXN], low[MAXN], idx, tot;
int stk[MAXN], top;
int size[MAXN << 1], w[MAXN << 1], root[MAXN << 1];
bool vis[MAXN << 1];
LL ans;
Graph G, T;

int main() {
	scanf("%d%d", &N, &M);
	tot = N;
	G.init(); T.init();
	for (int i = 0; i < M; ++i) {
		int u, v; scanf("%d%d", &u, &v);
		G.insert(u, v);
	}
	for (int i = 1; i <= N; ++i) {
		w[i] = -1, size[i] = 1;
		if (!dfn[i]) Tarjan(i, 0);
	}
	for (int i = 1; i <= tot; ++i)
		if (!vis[i]) {
			root[i] = i;
			dfs(i, 0);
		}
	for (int i = 1; i <= tot; ++i) ans += (LL)(size[root[i]] - size[i]) * size[i] * w[i];
	printf("%lld\n", ans << 1);

	return 0;
}
void Tarjan(int u, int fa) {
	dfn[u] = low[u] = ++idx;
	for (int i = G.head[u]; ~i; i = G.edge[i].next) {
		int v = G.edge[i].v;
		if (v == fa) continue;
		if (!dfn[v]) {
			stk[top++] = v;
			Tarjan(v, u);
			low[u] = std::min(low[u], low[v]);
			if (low[v] >= dfn[u]) {
				int p; ++tot;
				do {
					p = stk[--top];
					T.insert(tot, p);
					++w[tot];
				} while(p ^ v);
				T.insert(tot, u);
				++w[tot];
			}
		} else low[u] = std::min(low[u], dfn[v]);
	}
}
void dfs(int u, int fa) {
	vis[u] = 1;
	for (int i = T.head[u]; ~i; i = T.edge[i].next) {
		int v = T.edge[i].v;
		if (v == fa) continue;
		root[v] = root[u], dfs(v, u);
		ans += (LL)size[v] * size[u] * w[u];
		size[u] += size[v];
	}
}
//Rhein_E
posted @ 2019-03-28 10:29  Rhein_E  阅读(122)  评论(0编辑  收藏  举报