所驼门王的宝藏
题目背景:
分析:缩点 + 拓扑图上DP最长路
这个题,一看就知道是连边然后tarjan强连通缩点,然后直接DAG上DP最长路就可以了,重要的问题是如何建边,显然直接全部暴力建边时可能达到n2级别的边数的,然后我们来考虑优化,显然,对于同一行的1种类门,我们可以直接用其中的一个向所有的其他1号门连边,然后其他的分别向这一个连边,这样所有的1号就在一个强连通当中了,而对于这一行的其他门也只需要用其中的一个1号门向它们连边就可以了,因为所有1号门在同一强连通,所以其中之一能够到达,说明全部都能到达,对于同一列的2号门也可以类似处理,最后是三号门,三号门最多只有8条边,所以直接暴力连就可以了,至于如何找到周围是否存在门,可以暴力用map存储所有的门,也可以通过排序来找对应位置,代码写的是排序,然后最后来一发tarjan缩点 + DAG求最长路就可以了
Source:
/*
created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
const int MAXN = 1000000 + 10;
int n, r, c;
struct node {
int x, y, type, id;
} p[MAXN];
int exist[MAXN][3];
std::vector<int> edge[MAXN];
inline void add_edge(int x, int y) {
edge[x].push_back(y);
}
inline bool comp1(const node &a, const node &b) {
return (a.x == b.x) ? (a.type < b.type) : (a.x < b.x);
}
inline bool comp2(const node &a, const node &b) {
return (a.y == b.y) ? (b.type != 2 && (a.type == 2 || a.type < b.type))
: (a.y < b.y);
}
inline void read_in() {
scanf("%d%d%d", &n, &r, &c);
for (int i = 1; i <= n; ++i)
scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].type), p[i].id = i;
}
inline void build_graph() {
int pos = 0;
std::sort(p + 1, p + n + 1, comp1);
for (int i = 1; i <= n; ++i)
if (p[i].x != p[i - 1].x) pos = (p[i].type == 1) ? i : 0;
else if (pos == 0) continue ;
else {
add_edge(p[pos].id, p[i].id);
if (p[i].type == 1) add_edge(p[i].id, p[pos].id);
}
for (int i = p[1].x - 1, h1 = 1, h2 = 1, h3 = 1, last = 0, cur = 2;
i <= p[n].x; ++i) {
last = (last + 1) % 3, cur = (cur + 1) % 3;
for (; p[h1].x <= i + 1 && h1 <= n; ++h1)
if (p[h1].x == i + 1) exist[p[h1].y][cur] = p[h1].id;
for (; p[h2].x <= i && h2 <= n; ++h2)
if (p[h2].type == 3 && p[h2].x == i)
for (int j = p[h2].y - 1; j <= p[h2].y + 1; ++j)
for (int k = 0; k < 3; ++k)
if (exist[j][k] != 0 && exist[j][k] != p[h2].id)
add_edge(p[h2].id, exist[j][k]);
for (; p[h3].x <= i - 1 && h3 <= n; ++h3)
if (p[h3].x == i - 1) exist[p[h3].y][last] = 0;
}
std::sort(p + 1, p + n + 1, comp2);
for (int i = 1; i <= n; ++i)
if (p[i].y != p[i - 1].y) pos = (p[i].type == 2) ? i : 0;
else if (pos == 0) continue ;
else {
add_edge(p[pos].id, p[i].id);
if (p[i].type == 2) add_edge(p[i].id, p[pos].id);
}
}
std::vector<int> s;
int top, o, ind;
std::vector<int> new_edge[MAXN];
int low[MAXN], num[MAXN], scc[MAXN], scnt[MAXN], dp[MAXN];
bool vis[MAXN];
inline void tarjan(int cur) {
low[cur] = num[cur] = ++ind, vis[cur] = true, s.push_back(cur);
for (int p = 0; p < edge[cur].size(); ++p) {
int v = edge[cur][p];
if (num[v] == 0) tarjan(v), low[cur] = std::min(low[v], low[cur]);
else if (vis[v]) low[cur] = std::min(low[cur], num[v]);
}
if (low[cur] == num[cur]) {
++top;
do {
o = s.back(), vis[o] = false, scc[o] = top;
scnt[top]++, s.pop_back();
} while (o != cur);
}
}
inline int dfs(int cur) {
if (dp[cur] != 0) return dp[cur];
else dp[cur] = scnt[cur];
int temp = 0;
for (int p = 0; p < new_edge[cur].size(); ++p) {
int v = new_edge[cur][p];
temp = std::max(dfs(v), temp);
}
return dp[cur] += temp;
}
inline void solve() {
for (int i = 1; i <= n; ++i) if (num[i] == 0) tarjan(i);
// for (int i = 1; i <= n; ++i) {
// std::cout << i << '\n';
// for (int p = 0; p < edge[i].size(); ++p)
// std::cout << edge[i][p] << " ";
// std::cout << '\n';
// }
for (int i = 1; i <= n; ++i)
for (int p = 0; p < edge[i].size(); ++p) {
int v = edge[i][p];
if (scc[i] != scc[v]) new_edge[scc[i]].push_back(scc[v]);
}
int ans = 0;
for (int i = 1; i <= top; ++i) ans = std::max(dfs(i), ans);
std::cout << ans;
}
int main() {
read_in();
build_graph();
solve();
return 0;
}