点此看题面

大致题意: 一个由\(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. Codeforces#514D(三分,简单二维几何)

    #include<bits/stdc++.h>using namespace std;const double eps=1e-8;int n; struct node{    double ...

  2. LibreOJ #6192. 「美团 CodeM 复赛」城市网络

    #6192. 「美团 CodeM 复赛」城市网络 内存限制:64 MiB时间限制:500 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: sqc 提交提交记录统计讨论测试数据   题目描 ...

  3. 帝都Day5——依旧是数据结构

    /*Day1.Day2我尽量整理吧*/ 树状数组 树状数组滋瓷单点修改和前缀查询 加特技可以使得树状数组支持更多操作. c[2n+1]=a[2n+1](奇数就是它本身) c[2n]≠a[2n](偶数不 ...

  4. CentOS编译安装GCC 4.9.2成功

    在Linux上编译安装gcc是个寻烦恼的活,对于像我这样习惯于在Windows上面使用二进制安装包的人来说,自已编译安装gcc是个相当大的挑战,今天直接挑战最新版的gcc,是4.9.2版本的,做之前查 ...

  5. 5、python数据类型之元组(tuple)

    元组 元组和列表最大的区别是元组中的元素固定,元组不能修改,所以不能对元组进行增.删.改 1.创建 tu = (11,22,33) tu = tuple(11,22,33) tu = tuple([] ...

  6. HDU 5785 Interesting manacher + 延迟标记

    题意:给你一个串,若里面有两个相邻的没有交集的回文串的话,设为S[i...j] 和 S[j+1...k],对答案的贡献是i*k,就是左端点的值乘上右端点的值. 首先,如果s[x1....j].s[x2 ...

  7. MapReduce的输出格式

    1. OutputFormat接口 OutputFormat为输出格式接口,主要用于描述输出数据的格式,它能将输出的键值对写入特定格式的文件中.输出格式的层次结构如下 2. 文本输出 Hadoop默认 ...

  8. Git忽略提交规则

    Git忽略提交规则 转自:https://www.cnblogs.com/kevingrace/p/5690241.html 在使用Git的过程中,我们喜欢有的文件比如日志,临时文件,编译的中间文件等 ...

  9. IDEA使用汇总

    1. 常用配置 File --> Settings (Ctrl + Alt + S) 1).提示不区分大小写: Editor-->Genereal-->Code Completion ...

  10. 内核的执行头程序head.S

    功能 定义data段和text段 重新手动初始化gdt表, idt表, tss表结构 初始化页表和页目录 --> 页目录的数据放在一个页表中 在页目录中, 其实地址为0x1000, 初始化页目录 ...