题解 P2403 [SDOI2010]所驼门王的宝藏
Posted colazcy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P2403 [SDOI2010]所驼门王的宝藏相关的知识,希望对你有一定的参考价值。
Solution [SDOI2010]所驼门王的宝藏
题目大意:给定一个\(R\)行\(C\)列的矩阵,有些方格有宝藏和传送门.你可以从任意方格进入,到达有宝藏的宫室时可以横行任意传送、纵行任意传送、八连块任意传送(视传送门类型而定),问最多可以获得多少宝藏
分析:看到什么"传送"这类字眼我们多半就会想到图论.于是乎我们\(1min\)就可以想出一个暴力算法:
每个有宝藏的点向它可以到达的所有有宝藏的点连边,\(Tarjan\)缩点后求\(DAG\)最长路
大概有\(40\)分了?
我们看看问题出在哪儿:每个点向它所有可以到达的点连边,但是如果所有的点都在同一行,而且都为"横天门"的话,那你建边就要\(O(n ^ 2)\)的复杂度,不可接受
sb的我:啊!这是区间连边!可以线段树优化建图!
教练:醒一醒!线段树建图空间复杂度\(n^2\)
言归正传,我们最后都是要缩点的,根本没必要对于每个点都暴力连边.我们可以对于每行、每列都设置一个虚拟点,从虚拟点向当前行(列)每个点连边.然后对于每个点,我们向其行(列)虚拟点连边,这样从每个点出发还是可以走到当前行(列)所有点,但是边数大大减少
对于八联通块?我们可以用\(hash\)把所有有宝藏的点的坐标存起来,然后暴力找找连边即可
于是成型算法
- 行列设置虚拟点,向当前行(列)所有点连边
- 横天门向他所在的这一行虚拟点连一条边
- 纵寰门向他所在的这一列虚拟点连一条边
- w自w由w门w暴力连边
代码:
#include <cstdio>
#include <cctype>
#include <vector>
#include <stack>
#include <unordered_map>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 1e5;
int n,r,c;
inline int read()
int x = 0;char c = getchar();
while(!isdigit(c))c = getchar();
while(isdigit(c))x = x * 10 + c - '0',c = getchar();
return x;
int head[3 * maxn],nxt[3 * maxn],to[3 * maxn];
vector<int> line[maxn],column[maxn];
inline void addedge(int from,int to)
static int tot;
::to[++tot] = to;
nxt[tot] = head[from];
head[from] = tot;
namespace Tarjan//Tarjan板子
int dfn[2 * maxn],low[2 * maxn],instk[2 * maxn],col[2 * maxn],siz[2 * maxn],dfs_tot,col_tot;
stack<int> stk;
void tarjan(int u)
dfn[u] = low[u] = ++dfs_tot;
stk.push(u),instk[u] = 1;
for(int i = head[u];i;i = nxt[i])
int v = to[i];
if(!dfn[v])tarjan(v),low[u] = min(low[u],low[v]);
else if(instk[v])low[u] = min(low[u],dfn[v]);
if(low[u] == dfn[u])
int t;++col_tot;
do
t = stk.top();stk.pop(),instk[t] = 0;
col[t] = col_tot;
siz[col_tot] += t <= n;//只有那些有宝藏的点(编号小于等于n)的点才对答案有贡献
while(t != u);
using namespace Tarjan;
namespace Solve//dp
int head[2 * maxn],nxt[2 * maxn],to[2 * maxn];
inline void addedge(int from,int to)
static int tot;
Solve::to[++tot] = to;
nxt[tot] = head[from];
head[from] = tot;
int d[2 * maxn],val[2 * maxn],ans = -0x7fffffff;
void dfs(int u)
if(d[u])return;
d[u] = val[u];
for(int i = head[u];i;i = nxt[i])
int v = to[i];
dfs(v),d[u] = max(d[u],d[v] + val[u]);
ans = max(ans,d[u]);
struct Point
int x,y,opt;
point[maxn];
inline ll mhash(int x,int y)
return x * c + y;
unordered_map<ll,int> mp;//hash存图,懒得写直接STL
int main()
n = read(),r = read(),c = read();
for(int x,y,opt,i = 1;i <= n;i++)
point[i].x = x = read(),point[i].y = y = read(),point[i].opt = opt = read();
mp[mhash(x,y)] = i;
if(opt == 1)addedge(i,n + x);
if(opt == 2)addedge(i,n + r + y);//向虚拟点连边
line[x].push_back(i);
column[y].push_back(i);//保存每行(列)有哪些点
for(int i = 1;i <= r;i++)
for(int v : line[i])
addedge(n + i,v);
for(int i = 1;i <= c;i++)
for(int v : column[i])
addedge(n + r + i,v);//虚拟点连边
for(int i = 1;i <= n;i++)//w自w由w门w暴力连边
if(point[i].opt == 3)
for(int x = point[i].x - 1;x <= point[i].x + 1;x++)
for(int y = point[i].y - 1;y <= point[i].y + 1;y++)
if(x >= 1 && x <= r && y >= 1 && y <= c && (x != point[i].x || y != point[i].y) && mp.find(mhash(x,y)) != mp.end())
addedge(i,mp[mhash(x,y)]);
for(int i = 1;i <= n + r + c;i++)//缩点
if(!dfn[i])tarjan(i);
for(int u = 1;u <= n + r + c;u++)//在DAG新图上做dp
for(int i = head[u];i;i = nxt[i])
int v = to[i];
if(col[u] != col[v])
Solve::addedge(col[u],col[v]);
for(int i = 1;i <= col_tot;i++)//新图每个点的点权等于联通分量大小
Solve::val[i] = siz[i];
for(int i = 1;i <= col_tot;i++)//dp
Solve::dfs(i);
return printf("%d\n",Solve::ans),0;//Bye
那什么门是敏感词汇,醉了
以上是关于题解 P2403 [SDOI2010]所驼门王的宝藏的主要内容,如果未能解决你的问题,请参考以下文章