[APIO 2018] Duathlon 铁人两项(圆方树 + 算贡献) | 错题本

文章目录

题目

[APIO 2018] Duathlon 铁人两项

分析

考虑已知 s , f s, f ,则 c c 大致等于圆方树上 s f s \to f 的简单路径经过的点双的大小(即方点的度)之和。注意如果直接全部加起来会算多,因为圆方树上的非叶子圆点都是割点,即属于多个点双,而我路径上会将这些割点重复统计。解决办法很巧妙:方点的权值不变(所在点双的大小即度数),圆点的权值设置为 1 -1 。这样一来统计 s , f s, f 在圆方树上的简单路径的权值和即为已知 s , f s, f 的方案数。于是问题转化为求树上所有长度不小于 2 2 的简单路径的权值之和。

这个问题可以转化为,枚举 c c ,计算经过 c c 的路径条数 x c x_c ,则 c = 1 n w c x c \sum_{c = 1}^{n} w_c \cdot x_c w c w_c c c 的权值)即为答案,而 x c x_c 可以通过 DFS 计算子树大小并结合简单的乘法原理求得。

错因

  • 没有处理不连通的情况。

代码

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <stack>
#include <vector>

typedef long long LL;

const int LOG = 20;
const int MAXN = 500000;
const int MAXM = 1000000;

int N, M;
std::vector<int> G[MAXN + 5];

int SqrCnt;
std::vector<int> T[MAXN * 2 + 5];

void AddEdge(int u, int v) {
	T[u].push_back(v), T[v].push_back(u);
}

LL Ans, tot;
int W[MAXN + 5];
int Size[MAXN + 5];

void Dfs(int u, int fa) {
	Size[u] = u <= N;
	for (int i = 0; i < int(T[u].size()); i++) {
		int v = T[u][i];
		if (v != fa) {
			Dfs(v, u);
			Ans += 2ll * W[u] * Size[u] * Size[v];
			Size[u] += Size[v];
		}
	}
	Ans += (LL)2ll * W[u] * Size[u] * (tot - Size[u]);
}

std::stack<int> S;
int Dfn[MAXN + 5], Low[MAXN + 5], DfnCnt;

void Tarjan(int u, int fa) {
	S.push(u); ++tot;
	Dfn[u] = Low[u] = ++DfnCnt;
	for (int i = 0; i < int(G[u].size()); i++) {
		int v = G[u][i];
		if (v == fa)
			continue;
		if (!Dfn[v]) {
			Tarjan(v, u);
			Low[u] = std::min(Low[u], Low[v]);
			if (Low[v] >= Dfn[u]) {
				AddEdge(u, ++SqrCnt); W[SqrCnt]++;
				while (!S.empty()) {
					int x = S.top(); S.pop();
					AddEdge(x, SqrCnt); W[SqrCnt]++;
					if (x == v)
						break;
				}
			}
		}
		else
			Low[u] = std::min(Low[u], Dfn[v]);
	}
}

int main() {
	scanf("%d%d", &N, &M);
	for (int i = 1; i <= M; i++) {
		int u, v; scanf("%d%d", &u, &v);
		G[u].push_back(v), G[v].push_back(u);
	}
	SqrCnt = N;
	for (int i = 1; i <= N; i++)
		W[i] = -1;
	for (int i = 1; i <= N; i++) {
		if (!Dfn[i]) {
			tot = 0;
			Tarjan(i, 0);
			S.pop();
			Dfs(i, 0);
		}
	}
	printf("%lld", Ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/C20190102/article/details/107522777