Description

Input
第一行给出三个正整数 N, R, C。 以下 N 行,每行给出一扇传送门的信息,包含三个正整数xi, yi, Ti,表示该传送门设
在位于第 xi行第yi列的藏宝宫室,类型为 Ti。Ti是一个1~3间的整数, 1表示可以传送到第 xi行任意一列的“横天门”,
2表示可以传送到任意一行第 yi列的“纵寰门”,3表示可以传送到周围 8格宫室的“自由门”。 保证 1≤xi≤R,1≤yi≤C,
所有的传送门位置互不相同。
Output
只有一个正整数,表示你确定的路线所经过不同藏宝宫室的最大数目。
HINT
N <= 100000, R,C <= 1000000题解
此题的难点在于建图。将同一行,同一列的点连接到同一个点L上,再用map储存各个点的位置信息。对于横天门和纵寰门与L连接,自由门则通过查找其能到达的点是否在map中建边。之后用tarjan缩点,构成DAG,再用topsort进行最大值更新。
PS:我在bzoj上总是超时,我把数组开小点就过了。为什么它不报爆内存!!!!!
#include <cstdio>
#include <iostream>
#include <cstring>
#include <map>
#include <stack>
#include <algorithm>
using namespace std;
const int Maxn = 100005, MaxS = 1000005;
const int dx[] = {0,0,-1,-1,-1,1,1,1};
const int dy[] = {1,-1,-1,0,1,-1,0,1};
struct edge {
int to; edge *next;
}E[Maxn * 100];
int X[Maxn], Y[Maxn], T[Maxn], totedge = -1;
int dfs_time, per[Maxn], Belong[Maxn], Wealth[Maxn], Bcnt;
int in[Maxn], f[Maxn];
bool ins[Maxn];
edge *head[Maxn], *lx[MaxS], *ly[MaxS], *Edge[Maxn];
inline int readint(int& a) {
a = 0; char c = getchar(); int flag = 1;
while(c <'0' || c > '9') {
if (c == '-') flag = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
a = (a << 3) + (a << 1) + c - '0';
c = getchar();
}
return a;
}
typedef pair<int,int> pii;
#define MK make_pair
map<pii,int>S;
void adde(const int& a,const int& b) {
edge *ne = E + (++totedge);
ne->to = b; ne->next = head[a];
head[a] = ne;
}
void addE(const int& a,const int& b) {
edge *ne = E + (++totedge);
ne->to = b; ne->next = Edge[a];
Edge[a] = ne;
}
void Link(const int& a) {
edge *ne = E + (++totedge);
ne->to = a; ne->next = lx[X[a]];
lx[X[a]] = ne;
ne = E + (++totedge);
ne->to = a; ne->next = ly[Y[a]];
ly[Y[a]] = ne;
}
stack<int>s;
int tarjan(const int& u) {
int lowu = per[u] = ++dfs_time;
s.push(u); ins[u] = true;
for (edge *i = head[u]; i; i = i->next) {
if (!per[i->to]) lowu = min(lowu,tarjan(i->to));
else if(ins[i->to]) lowu = min(lowu,per[i->to]);
}
if (lowu == per[u]) {
int v; ++Bcnt;
while(1) {
v = s.top(); s.pop();
ins[v] = false; Belong[v] = Bcnt;
++Wealth[Bcnt];
if (v == u) break;
}
}
return lowu;
}
int topsort() {
stack<int>ss;
while (!ss.empty()) ss.pop();
int ans = -1, u;
for (int i = 1; i <= Bcnt; ++i) if (!in[i]){
ss.push(i); f[i] = Wealth[i];
}
while (!ss.empty()) {
u = ss.top(); ss.pop();
if (f[u] > ans) ans = f[u];
for (edge *it = Edge[u]; it; it = it->next) {
if (!(--in[it->to])) ss.push(it->to);
f[it->to] = max(f[it->to],f[u] + Wealth[it->to]);
}
}
return ans;
}
int main() {
int N, R, C;
readint(N), readint(R), readint(C);
for (int i = 1; i <= N; ++i) {
readint(X[i]); readint(Y[i]); readint(T[i]);
Link(i); S[MK(X[i],Y[i])] = i;
}
for (int i = 1; i <= N; ++i) {
if (T[i] == 1) {
for (edge *it = lx[X[i]]; it; it = it->next) {
if (it->to == i) continue;
adde(i,it->to);
}
}else if(T[i] == 2) {
for (edge *it = ly[Y[i]]; it; it = it->next) {
if (it->to == i) continue;
adde(i,it->to);
}
}else {
int _x, _y; map<pii,int>::iterator it;
for (int j = 0; j < 8; ++j) {
_x = X[i] + dx[j], _y = Y[i] + dy[j];
it = S.find(MK(_x,_y));
if (it == S.end()) continue;
adde(i,it->second);
}
}
}
for (int i = 1; i <= N; ++i) if(!per[i]) tarjan(i);
for (int i = 1; i <= N; ++i) {
for (edge *it = head[i]; it; it = it->next) {
if (Belong[i] != Belong[it->to]) {
addE(Belong[i],Belong[it->to]); ++in[Belong[it->to]];
}
}
}
cout << topsort() << endl;
return 0;
}

博客围绕一道传送门题目展开,给出题目描述、输入输出要求及提示。题解指出难点在建图,需将同行同列点连到点L,用map存位置信息。横天门和纵寰门与L连接,自由门查找可达点建边,之后用tarjan缩点成DAG,再用topsort更新最大值。
1801

被折叠的 条评论
为什么被折叠?



