【SDOI2017】天才黑客

题面

题解

这是好久之前菊开讲的一道题目了。

可以发现在这道题目中,边比点更加重要,所以我们化边为点,将边权改为点权,边与边之间的边权就是题目所给的Trie树上LCA深度的和。

想到一个平方的暴力,每条边和它连向的点的出边连一条边。下一步考虑怎么优化。

对于每一个点,将它的入边和出边都拿出来,按照dfs序排序,那么可以考虑将每个点拆成入点和出点,因为\(\operatorname{lcp}(i, j)\)的值是\(i \to j\)之间所有的边的\(\operatorname{lcp}\)的最小值,于是前后缀优化连边就可以了。

可以考虑使用下面这个图来帮助理解:

graph.png

说起来容易做起来难,代码留给读者作为练习

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)

inline int read()
{
	int data = 0, w = 1; char ch = getchar();
	while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
	if (ch == '-') w = -1, ch = getchar();
	while (ch >= '0' && ch <= '9') data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

const int N(50010);
struct edge { int to, dis; };
int T, n, m, K, val[N * 20], pos[N], dis[N * 20], vis[N * 20], cur;
std::vector<int> in[N], out[N];
std::vector<edge> G[N * 20];
namespace Tree
{
	int fa[N], size[N], heavy[N], dfn[N], bel[N], dep[N], cnt; std::vector<int> T[N];
	inline void Add(int x, int y) { T[x].push_back(y); }
	void dfs(int x)
	{
		size[x] = 1, heavy[x] = 0;
		for (auto i : T[x])
		{
			fa[i] = x, dep[i] = dep[x] + 1, dfs(i), size[x] += size[i];
			if (size[heavy[x]] < size[i]) heavy[x] = i;
		}
	}

	void dfs(int x, int chain)
	{
		dfn[x] = ++cnt, bel[x] = chain;
		if (heavy[x]) dfs(heavy[x], chain);
		for (auto i : T[x]) if (i != heavy[x]) dfs(i, i);
	}

	int LCA(int x, int y)
	{
		for (; bel[x] != bel[y]; x = fa[bel[x]])
			if (dfn[bel[x]] < dfn[bel[y]]) std::swap(x, y);
		return dfn[x] < dfn[y] ? dep[x] : dep[y];
	}
}

void Dijkstra()
{
	std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int> >,
		std::greater<std::pair<int, int> > > Q;
	memset(dis, 127, (cur + 1) << 2), memset(vis, 0, sizeof vis);
	for (auto i : out[1]) Q.push(std::make_pair(dis[i] = val[i], i));
	while (!Q.empty())
	{
		int x = Q.top().second; Q.pop(); if (vis[x]) continue; vis[x] = 1;
		for (auto i : G[x]) if (dis[x] + val[i.to] + i.dis < dis[i.to])
			Q.push(std::make_pair(dis[i.to] = dis[x] + val[i.to] + i.dis, i.to));
	}
}

int Abs(int x) { return x < 0 ? -x : x; }
int cmp(int x, int y) { return Tree::dfn[pos[Abs(x)]] < Tree::dfn[pos[Abs(y)]]; }
void Link(int x)
{
	static int stk[N], p1[N], p2[N], q1[N], q2[N]; int top = 0;
	for (auto i : in[x]) stk[++top] = i;
	for (auto i : out[x]) stk[++top] = -i;
	std::sort(stk + 1, stk + top + 1, cmp);
	for (int i = 1; i <= top; i++)
		p1[i] = ++cur, p2[i] = ++cur, q1[i] = ++cur, q2[i] = ++cur;
	for (int i = 2; i <= top; i++)
		G[p1[i - 1]].push_back((edge) {p1[i], 0}),
		G[q1[i - 1]].push_back((edge) {q1[i], 0}),
		G[p2[i]].push_back((edge) {p2[i - 1], 0}),
		G[q2[i]].push_back((edge) {q2[i - 1], 0});
	for (int i = 1; i <= top; i++)
		if (stk[i] > 0) G[stk[i]].push_back((edge) {p1[i], 0}),
						G[stk[i]].push_back((edge) {p2[i], 0});
		else stk[i] = -stk[i], G[q1[i]].push_back((edge) {stk[i], 0}),
							   G[q2[i]].push_back((edge) {stk[i], 0});
	for (int i = 1, x; i < top; i++)
		x = Tree::LCA(pos[stk[i]], pos[stk[i + 1]]),
		G[p1[i]].push_back((edge) {q1[i + 1], x}),
		G[p2[i + 1]].push_back((edge) {q2[i], x});
}

int main()
{
#ifndef ONLINE_JUDGE
	file(cpp);
#endif
	for (T = read(); T--; )
	{
		n = read(), m = cur = read(), K = read();
		memset(val, 0, sizeof val), Tree::cnt = 0;
		for (int i = 1, x, y; i <= m; i++)
			x = read(), y = read(), val[i] = read(), pos[i] = read(),
			out[x].push_back(i), in[y].push_back(i);
		for (int i = 1, x, y; i < K; i++)
			x = read(), y = read(), read(), Tree::Add(x, y);
		Tree::dfs(1), Tree::dfs(1, 1);
		for (int i = 2; i <= n; i++) Link(i);
		Dijkstra();
		for (int i = 2; i <= n; i++)
		{
			int ans = 2e9;
			for (auto x : in[i]) ans = std::min(ans, dis[x]);
			printf("%d\n", ans);
		}
		for (int i = 1; i <= cur; i++) G[i].clear();
		for (int i = 1; i <= n; i++) in[i].clear(), out[i].clear();
		for (int i = 1; i <= K; i++) Tree::T[i].clear();
	}
	return 0;
}
posted @ 2019-10-16 20:10  xgzc  阅读(185)  评论(3编辑  收藏  举报