点此看题面

大致题意: 一个由\(R*C\)间矩形宫室组成的宫殿中的\(N\)间宫室里埋藏着宝藏。由一间宫室到达另一间宫室只能通过传送门,且只有埋有宝藏的宫室才有传送门。传送门分为3种,分别可以到达同行的任一宫室(横天门)同列的任一宫室(纵寰门)以该宫室为中心周围8个的任一宫室(自 由 门)。现在你可以从任一宫室开始寻宝,并可以在任一宫室结束寻宝,请求出最多可获得的宝藏数目(每个宝藏只能获得一次)。

一个简单的想法

显然,我们可以将每个宫室与它能到达的宫室之间连一条边。由于可能会出现环,我们就需要用\(Tarjan\)来把环缩点,缩点的过程中要注意记录每个环上的宫室数目。

缩完点后,我们就可以从每个入度为1的点开始对图进行遍历,求出最多能走过的宫室数。而这个步骤可以用dfs轻松实现。

这样就好了吗?

不,还有一些细节。

小技巧优化

首先,我们要注意的是,直接把每个宫室与它能到达的宫室之间两两建边,建出的边的规模是\(O(N^2)\)的,而\(N≤100000\),这么多边我们存不下。虽然洛谷数据水,这样也能过。

这时就要用到一个小技巧:对于同一行的横天门,我们不需要将其两两之间连边,只要保证最后能够连成一个即可;而该行其他类型的门,也不需要将每一个横天门向其连边,只要让第一个出现的横天门向所有其他类型的门连边即可。同理,对于同一行的纵寰门,也可以进行同样的处理,让边数从\(O(n^2)\)降到了\(O(n)\)大大减少了边数。

还有,就算你进行了这样的操作,最后还是有可能会TLE,这时,我们可以发现,好像时间复杂度最大的就是最后的dfs遍历了。我们可以考虑用记忆化,用\(vist[i]\)来记录从缩点后编号为\(i\)的点出发,最多能得到的宝物数目即可。

这样,就可以AC了。

代码

#include<bits/stdc++.h>
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define LL long long
#define N 100000
using namespace std;
typedef pair<int,int> point;
int n,r,c,d=0,ee=0,nee=0,cnt=0,ans=0,top=0,lnk[N+5],nlnk[N+5],dfn[N+5],low[N+5],vis[N+5],Stack[N+5],sum[N+5],In[N+5],vist[N+5];
struct door
{
int x,y,Type,pos,col;
}a[N+5];
struct edge
{
int to,nxt;
}e[2*N+5],ne[2*N+5];
map<point,int> mp;
inline char tc()
{
static char ff[100000],*A=ff,*B=ff;
return A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
x=0;int f=1;char ch;
while(!isdigit(ch=tc())) if(ch=='-') f=-1;
while(x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
x*=f;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
inline void add(int x,int y)//添加一条新边
{
if(x^y) e[++ee].to=y,e[ee].nxt=lnk[x],lnk[x]=ee;
}
inline void nadd(int x,int y)//添加一条缩点后的新边
{
if(x^y) ne[++nee].to=y,ne[nee].nxt=nlnk[x],nlnk[x]=nee;
}
inline bool cmp_x(door x,door y)//以行为第一关键字进行sort,使每一行都是横天门在最前面
{
if(x.x^y.x) return x.x<y.x;//判断是否在同一行,不是同一行的尽量让行小的在前面
if(x.Type==1&&y.Type^1) return true;//对于同行的,让横天门在最前面
if(x.Type^1&&y.Type==1) return false;
return x.y<y.y;//如果都是或都不是横天门,则列小的在前面
}
inline bool cmp_y(door x,door y)//以列为第一关键字进行sort,使每一列都是纵寰门在最前面
{
if(x.y^y.y) return x.y<y.y;//判断是否在同一列,不是同一列的尽量让列小的在前面
if(x.Type==2&&y.Type^2) return true;//对于同列的,让纵寰门在最前面
if(x.Type^2&&y.Type==2) return false;
return x.x<y.x;//如果都是或都不是纵寰门,则行小的在前面
}
inline bool cmp_z(door x,door y)//对自 由 门的特殊处理,让自 由 门在最前面
{
if(x.Type==3&&y.Type^3) return true;//让自 由 门在最前面
if(x.Type^3&&y.Type==3) return false;
return x.pos<y.pos;//如果都是或都不是自 由 门,则编号小的在前面
}
inline bool cmp_pos(door x,door y)//以编号为第一关键字进行sort,让编号小的在前面,变回读入时的顺序
{
return x.pos<y.pos;
}
inline void Tarjan(int x)//利用Tarjan缩点
{
dfn[x]=low[x]=++d,Stack[++top]=x,vis[x]=1;//记录当前节点的dfs序与当前节点所能到达的dfs序最小的点,将当前节点加入栈中,并标记当前节点在栈中
for(register int i=lnk[x];i;i=e[i].nxt)//枚举从当前节点出发的每一条边
{
if(!dfn[e[i].to]) Tarjan(e[i].to),low[x]=min(low[x],low[e[i].to]);//如果这个节点没访问过,就先对这个节点进行操作,然后更新当前节点能到达的dfs序最小的点
else if(vis[e[i].to]) low[x]=min(low[x],low[e[i].to]);//否则,如果这个点在栈中,就进行更新
}
if(low[x]==dfn[x])//如果当前节点就是当前节点能到达的dfs序最小的点,则对当前强连通分量进行缩点
{
sum[a[x].col=++cnt]=1,vis[x]=0;//给当前节点加入一个新的强连通分量,并将这个新的强连通分量的大小赋值为1,标记当前节点已出栈
while(Stack[top]^x) ++sum[a[Stack[top]].col=cnt],vis[Stack[top--]]=0;//将栈中当前节点之上的节点一一弹出,并更新这个新的强连通分量的大小
--top;//将当前节点弹出
}
}
inline void dfs(int x)//dfs遍历缩点后的图
{
for(register int i=nlnk[x];i;i=ne[i].nxt)//枚举每一条边
{
if(!vist[ne[i].to]) dfs(ne[i].to);//如果这个节点未被访问过,就访问该节点
vist[x]=max(vist[x],vist[ne[i].to]+sum[ne[i].to]);//更新从当前节点出发能得到的最多的宝物数目
}
}
int main()
{
register int i,j;
for(read(n),read(r),read(c),i=1;i<=n;++i)
read(a[i].x),read(a[i].y),read(a[i].Type),a[i].pos=i,mp[make_pair(a[i].x,a[i].y)]=i;
int fst;
sort(a+1,a+n+1,cmp_x);//对接下来对横天门的操作的预处理
for(i=1,fst=0;i<=n;++i)
{
while(i<=n&&a[i].Type^1) ++i;//只要当前门不是横天门,就跳过这个门
if(i>n) continue;
fst=i;//将i标记为该行第一个横天门
while(i<=n&&a[i].x==a[i+1].x&&a[i+1].Type==1) add(a[i].pos,a[i+1].pos),++i;//将前一个横天门与当前横天门连边
add(a[i].pos,a[fst].pos);//将最后一个横天门与第一个横天门连边,形成一个环
while(i<=n&&a[i].x==a[i+1].x) add(a[fst].pos,a[++i].pos);//将该行第一个横天门与该行其他类型的门连边
}
sort(a+1,a+n+1,cmp_y);//对接下来对纵寰门的操作的预处理
for(i=1,fst=0;i<=n;++i)
{
while(i<=n&&a[i].Type^2) ++i;//只要当前门不是纵寰门,就跳过这个门
if(i>n) continue;
fst=i;//将i标记为该列第一个纵寰门
while(i<=n&&a[i].y==a[i+1].y&&a[i+1].Type==2) add(a[i].pos,a[i+1].pos),++i;//将前一个纵寰门与当前纵寰门连边
add(a[i].pos,a[fst].pos);//将最后一个纵寰门与第一个纵寰门连边,形成一个环
while(i<=n&&a[i].y==a[i+1].y) add(a[fst].pos,a[++i].pos);//将该列第一个纵寰门与该列其他类型的门连边
}
sort(a+1,a+n+1,cmp_z);//对接下来对自 由 门的操作的预处理
for(i=1;i<=n&&a[i].Type==3;++i)//枚举每一个自 由 门
{
//枚举每个自 由 门周围的8个宫室,将这个门与周围有宝藏的宫室相连
if(mp[make_pair(a[i].x-1,a[i].y)]) add(a[i].pos,mp[make_pair(a[i].x-1,a[i].y)]);
if(mp[make_pair(a[i].x+1,a[i].y)]) add(a[i].pos,mp[make_pair(a[i].x+1,a[i].y)]);
if(mp[make_pair(a[i].x,a[i].y-1)]) add(a[i].pos,mp[make_pair(a[i].x,a[i].y-1)]);
if(mp[make_pair(a[i].x,a[i].y+1)]) add(a[i].pos,mp[make_pair(a[i].x,a[i].y+1)]);
if(mp[make_pair(a[i].x-1,a[i].y-1)]) add(a[i].pos,mp[make_pair(a[i].x-1,a[i].y-1)]);
if(mp[make_pair(a[i].x+1,a[i].y+1)]) add(a[i].pos,mp[make_pair(a[i].x+1,a[i].y+1)]);
if(mp[make_pair(a[i].x+1,a[i].y-1)]) add(a[i].pos,mp[make_pair(a[i].x+1,a[i].y-1)]);
if(mp[make_pair(a[i].x-1,a[i].y+1)]) add(a[i].pos,mp[make_pair(a[i].x-1,a[i].y+1)]);
}
sort(a+1,a+n+1,cmp_pos);//按照读入时的顺序重新排序
for(i=1;i<=n;++i)
if(!dfn[a[i].pos]) Tarjan(i);//用Tarjan缩点
for(i=1;i<=n;++i)
for(j=lnk[i];j;j=e[j].nxt)
if(a[i].col^a[e[j].to].col) nadd(a[i].col,a[e[j].to].col),++In[a[e[j].to].col];//更新缩点之后点与点之间的边
for(i=1;i<=cnt;++i)
if(!In[i]) dfs(i),ans=max(ans,vist[i]+sum[i]);//贪心的思想,从入度为0的点出发肯定能得到最优答案
return write(ans),0;
}

【洛谷2403】[SDOI2010] 所驼门王的宝藏(Tarjan+dfs遍历)的更多相关文章

  1. 洛谷 2403 [SDOI2010] 所驼门王的宝藏

    题目描述 在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族.被族人恭称为“先知”的Alpaca L. Sotomon是这个家族的领袖,外人也称其为“所驼门王”.所驼门王毕生致力于维护家族的安定与和谐, ...

  2. 洛谷 P2403 [SDOI2010]所驼门王的宝藏 题解

    题目描述 分析 先放一张图便于理解 这一道题如果暴力建图会被卡成\(n^{2}\) 实际上,在我们暴力建图的时候,有很多边都是重复的 假如一行当中有许多横天门的话,我们就不必要把这一行当中的所有点和每 ...

  3. 洛咕 P2403 [SDOI2010]所驼门王的宝藏

    简单tarjan. 一行的横天门如果暴力连边会被卡成平方,所以只要相邻两个横天门连双向边,再随便选一个横天门向整行连边即可.纵寰门同理.ziyou门直接map暴力连边. 然后tarjan直接dp. / ...

  4. Luogu 2403 [SDOI2010]所驼门王的宝藏

    BZOJ 1924 内存要算准,我MLE了两次. 建立$n + r + c$个点,对于一个点$i$的坐标为$(x, y)$,连边$(n + x, i)$和$(n + r + y, i)$,代表这一列和 ...

  5. 「BZOJ1924」「SDOI2010」 所驼门王的宝藏 tarjan + dp(DAG 最长路)

    「BZOJ1924」[SDOI2010] 所驼门王的宝藏 tarjan + dp(DAG 最长路) -------------------------------------------------- ...

  6. [BZOJ 1924][Sdoi2010]所驼门王的宝藏

    1924: [Sdoi2010]所驼门王的宝藏 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 1285  Solved: 574[Submit][Sta ...

  7. 【题解】SDOI2010所驼门王的宝藏(强连通分量+优化建图)

    [题解]SDOI2010所驼门王的宝藏(强连通分量+优化建图) 最开始我想写线段树优化建图的说,数据结构学傻了233 虽然矩阵很大,但是没什么用,真正有用的是那些关键点 考虑关键点的类型: 横走型 竖 ...

  8. 【BZOJ-1924】所驼门王的宝藏 Tarjan缩点(+拓扑排序) + 拓扑图DP

    1924: [Sdoi2010]所驼门王的宝藏 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 787  Solved: 318[Submit][Stat ...

  9. [SDOI2010]所驼门王的宝藏

    题目描述 在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族.被族人恭称为"先知"的Alpaca L. Sotomon是这个家族的领袖,外人也称其为"所驼门王". ...

  10. [LuoguP2403][SDOI2010]所驼门王的宝藏

    题目描述 在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族.被族人恭称为"先知"的Alpaca L. Sotomon是这个家族的领袖,外人也称其为"所驼门王". ...

随机推荐

  1. 反射实现数据库增删改查DAO及DAOImpl源代码(二)

    配置文件源码 配置文件主要用于配置数据库对象(javaBean),配置表名,配置查询条件,配置删除条件 文件名称:objectConfigPath.properties 这个配置文件里面配置的是另外一 ...

  2. cf837E(xjb)

    题目链接:http://codeforces.com/problemset/problem/837/E 题意:f(a, 0) = 0 ,     f(a, b) = 1 + f(a, b - gcd( ...

  3. WampServer的安装

    首先安装好Microsoft Visual C++ 然后再安装WampServer 安装过程很简单 错误解决 运行后为黄色图标(成功运行应该为绿色图标) 解决办法: 1.80端口是否被占用 你的80端 ...

  4. Java: 面向对象程序设计(下)

    1. 类的继承 继承的意义: 当一个类A继承了一个已存在的类B后,类A就用于了类B所有的非private的属性和方法,但同时类A还可以根据需要,添加新的方法和属性. 在Java语言中,一个类可以拥有多 ...

  5. [sql Server]除非另外还指定了TOP 或 FOR XML,否则,ORDER BY 子句在视图、内联函数、派生表、子查询和公用表表达式中无效

    今天遇到一个奇怪的问题,项目突然要从mysql切换到sql server数据库,包含order by 子句的嵌套子查询报错. 示例:select top 10 name,age,sex from ( ...

  6. 洛谷P1318 积水面积

    题目描述 一组正整数,分别表示由正方体叠起的柱子的高度.若某高度值为\(x\),表示由\(x\)个正立方的方块迭起(如下图,\(0<=x<=5000\)).找出所有可能积水的地方(图中蓝色 ...

  7. 关于dijkstra求最短路(模板)

    嗯....   dijkstra是求最短路的一种算法(废话,思维含量较低,   并且时间复杂度较为稳定,为O(n^2),   但是注意:!!!!         不能处理边权为负的情况(但SPFA可以 ...

  8. mongoose的安装与使用(书签记录) 2017

    Windows7环境下安装:慕课网中的手记 http://www.imooc.com/article/12447 关于在CMD中对Mongo API的调用,从这些API调用中我们也可以更容易地理解在n ...

  9. ACM-ICPC 2018 南京赛区网络预赛 Lpl and Energy-saving Lamps (线段树:无序数组找到第一个小于val)

    题意:n个房间,每个房间有ai盏旧灯,每个月可以买m盏新灯,要求:按房间顺序换灯,如果剩下的新灯数目大于ai,那么进行更换,否则跳过该房间,判断下一个房间.如果所有房间都换完灯,那么久不会再买新灯. ...

  10. JPA规范基础 ppt教程

    https://wenku.baidu.com/view/5ca6ce6a1eb91a37f1115cee.html