洛谷 P2403 所驼门王的宝藏
洛谷 P2403 所驼门王的宝藏
题意
有一个 \(R\times C\) 的网格图,有 \(N\) 个点有传送门和宝藏。
有三种传送门:
一种可以传送至同一行的任意点,
一种可以传送到同一列的任一点,
一种可以传送的周围八个点。
可从任意点开始,任意点结束,求最多走过多少个宝藏。
思路
把网格图建成图,缩点后跑拓扑排序求出最长路即可。
但有个问题,若一行上全部都是种类 \(1\) 或一列上全部都是种类 \(2\),最多可建出 \(O(n^2)\) 条边。
需要优化建图。我们可以对每一行和每一列建一个虚点连向该行,该列所有的点。
若该传送门是种类 \(1\) 或种类 \(2\),将其连向该行或该列的虚点。
这样等价于连向每一个点,但减少了边数。
只需 \(O(n)\) 条边即可建出等价的图。
动态开点,要使用时再建立,不使用就不建,可以节省空间。
注意虚点不能算入最后的答案,没有传送门的点也不能。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int tot, ver[N << 1], nxt[N << 1], head[N];
int n, r, c, point, in[N], out[N];
int sc, scc[N], stk[N], top, val[N];
int low[N], dfn[N], siz[N], cnt;
bool instk[N];
int xz[] = {1, 0, -1, 0, 1, 1, -1, -1};
int yz[] = {0, 1, 0, -1, 1, -1, 1, -1};
map <pair<int, int>, int> GID;
map <int, int> HID;
map <int, int> LID;
vector <int> E[N];
queue <int> Q;
int dp[N], ans;
void add(int x, int y) {
ver[++ tot] = y;
nxt[tot] = head[x];
head[x] = tot;
}
int H(int x) { // 动态开点
if (HID.find(x) != HID.end()) return HID[x];
HID[x] = ++ point;
return point;
}
int L(int x) {
if (LID.find(x) != LID.end()) return LID[x];
LID[x] = ++ point;
return point;
}
int id(int x, int y) {
if (GID.find({x, y}) != GID.end())
return GID[{x, y}];
GID[{x, y}] = ++ point;
int id = point;
add(H(x), id); // 虚点连向每个点
add(L(y), id);
return id;
}
void tarjan(int x) {
dfn[x] = low[x] = ++ cnt;
stk[++ top] = x;
instk[x] = 1;
for (int i = head[x]; i; i = nxt[i]) {
int y = ver[i];
if (!dfn[y]) {
tarjan(y);
low[x] = min(low[x], low[y]);
}else if (instk[y])
low[x] = min(low[x], dfn[y]);
}
if (dfn[x] == low[x]) {
sc ++;
while (stk[top] != x) {
instk[stk[top]] = 0;
scc[stk[top]] = sc;
siz[sc] += val[stk[top]]; // 加上宝藏个数,不是1
top --;
}
instk[stk[top]] = 0;
scc[stk[top]] = sc;
siz[sc] += val[stk[top]];
top --;
}
}
void init() {
cin >> n >> r >> c;
for (int i = 1, x, y, t; i <= n; i ++) {
cin >> x >> y >> t;
int ID = id(x, y);
val[ID] = 1; // 有宝藏
if (t == 1) {
add(ID, H(x)); // 连向虚点
} else if (t == 2) {
add(ID, L(y));
} else if (t == 3) {
for (int j = 0; j < 8; j ++) {
int xx = x + xz[j], yy = y + yz[j];
if (xx < 1 || xx > r || yy < 1 || yy > c) continue;
add(ID, id(xx, yy));
}
}
}
}
void tarjan() { // 缩点
for (int i = 1; i <= point; i ++)
if (!dfn[i]) tarjan(i);
for (int i = 1; i <= point; i ++)
for (int j = head[i]; j; j = nxt[j])
if (scc[i] != scc[ver[j]]) {
E[scc[i]].push_back(scc[ver[j]]);
in[scc[ver[j]]] ++;
out[scc[i]] ++;
}
}
void tuopu() { // 拓扑
for (int i = 1; i <= sc; i ++)
if (!in[i]) {
Q.push(i);
dp[i] = siz[i];
}
while (!Q.empty()) {
int x = Q.front(); Q.pop();
for (auto y : E[x]) {
dp[y] = max(dp[y], dp[x] + siz[y]);
if (!(-- in[y])) Q.push(y);
}
}
for (int i = 1; i <= sc; i ++)
ans = max(ans, dp[i]);
}
int main() {
init(); tarjan(); tuopu();
cout << ans << "\n";
return 0;
}
本文来自博客园,作者:maniubi,转载请注明原文链接:https://www.cnblogs.com/maniubi/p/18393832,orz