ACM-ICPC 2018 徐州赛区网络预赛 J.Maze Designer
标签
- 生成树
- 树剖LCA
前言
- 还好,不难,就是没做过类似的题,一时间没弄明白题目在问什么…
简明题意
- 有一个n * m个排成矩形的节点,题目会给出这些点相互之间的连接,以及边权。每连一条边需要一个花费。而题目是这样要求的:每连一条边需要一个花费,而没有连的边是可以相互到达的,现在需要所有的点两两间存在唯一路径到达且建边的花费最小,在此前提下,给出q组询问,每组询问两点之间的步数。
思路
- 首先,如果说两两之间存在唯一路径,也就是说说有的点形成了一棵树
- 其次,思考树是怎么形成的?一开始,是一张图,现在需要去掉一些边,使成为一棵树,且去掉的边权之和需要最小
- 那么我们倒过来想,既然要成一棵树,我们直接想哪些边不用去掉?生成树上的边不用去掉,显然生成树上的边权越大,去掉的边权值和也就最小了,所以我们应该去跑一棵最大生成树
- 跑完生成树,需要询问两点之间的简单路径长。那么dfs预处理,很简单
注意事项
- 无
总结
- 如果说点两两之间存在唯一路径,也就是说所有的点形成了一棵树
- 对于一个图上的生成树,题目可能问你生成树的最大/小权,也可能让你最大/小化删去的边权。在这一题中,题目把边说成是强,需要你造墙,这就可能让人产生错觉,以为造墙就是造边,而实际上,造墙是去掉边
AC代码
#include<cstdio>
#include<vector>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 500 * 500 + 10;
struct Edge
{
int u, v, w;
Edge(int u, int v, int w) : u(u), v(v), w(w) {}
bool operator < (const Edge& a)const
{
return w < a.w;
}
};
int n, m;
vector<int> g[maxn];
priority_queue<Edge> que;
int fa0[maxn];
int __find(int u)
{
if (fa0[u] == 0)
return u;
return fa0[u] = __find(fa0[u]);
}
void __union(int u, int v)
{
int pa = __find(u), pb = __find(v);
if (pa != pb)
fa0[pa] = pb;
}
int getid(int i, int j)
{
return j + (i - 1) * m;
}
int dep[maxn], fa[maxn], siz[maxn], son[maxn], dis[maxn];
void dfs1(int u, int f, int deep, int tot)
{
dep[u] = deep;
fa[u] = f;
siz[u] = 1;
dis[u] = tot;
int max_son = -1;
for (auto& v : g[u])
{
if (v != f)
{
dfs1(v, u, deep + 1, tot + 1);
siz[u] += siz[v];
if (siz[v] > max_son)
max_son = siz[v], son[u] = v;
}
}
}
int top[maxn], id[maxn], cnt;
void dfs2(int u, int topf)
{
top[u] = topf;
id[u] = ++cnt;
if (son[u])
{
dfs2(son[u], topf);
for (auto& v : g[u])
if (v != fa[u] && v != son[u])
dfs2(v, v);
}
}
int LCA(int u, int v)
{
while (top[u] != top[v])
{
if (dep[top[v]] < dep[top[u]]) swap(u, v);
v = fa[top[v]];
}
return dep[u] < dep[v] ? u : v;
}
bool can_use[maxn];
void solve()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
char cmd[10];
int w;
scanf("%s%d", cmd, &w);
if (cmd[0] == 'D')
{
que.push(Edge(getid(i, j), getid(i + 1, j), w));
}
scanf("%s%d", cmd, &w);
if (cmd[0] == 'R')
{
que.push(Edge(getid(i, j), getid(i, j + 1), w));
}
}
//kruskal
while (!que.empty())
{
int u = que.top().u, v = que.top().v, w = que.top().w;
que.pop();
if (__find(u) == __find(v))
continue;
g[u].push_back(v), g[v].push_back(u);
can_use[u] = 1, can_use[v] = 1;
__union(u, v);
}
//树剖
dfs1(1, 1, 1, 0);
dfs2(1, 1);
//处理询问
int q;
scanf("%d", &q);
while (q--)
{
int a, b, c, d;
scanf("%d%d%d%d", &a, &b, &c, &d);
int u = getid(a, b), v = getid(c, d);
printf("%d\n", dis[u] + dis[v] - 2 * dis[LCA(u, v)]);
}
}
int main()
{
freopen("Testin.txt", "r", stdin);
solve();
return 0;
}