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

-----------------------------------------------------------------------------------------------------------------------------------------

在宽广的非洲荒漠中,生活着一群勤劳勇敢的羊驼家族。被族人恭称为“先知”的 Alpaca L. Sotomon 是这个家族的领袖,外人也称其为“所驼门王”。所驼门王毕生致力于维护家族的安定与和谐,他曾亲自率军粉碎河蟹帝国主义的野蛮侵略,为族人立下赫赫战功。所驼门王一生财宝无数,但因其生性节俭低调, 他将财宝埋藏在自己设计的地下宫殿里,这也是今天 Henry Curtis 故事的起点。Henry 是一个爱财如命的贪婪家伙,而又非常聪明,他费尽心机谋划了这次盗窃行动,破解重重机关后来到这座地下宫殿前。

-----------------------------------------------------------------------------------------------------------------------------------------

整座宫殿呈矩阵状,由 R×C 间矩形宫室组成,其中有 N 间宫室里埋藏着宝藏,称作藏宝宫室。宫殿里外、相邻宫室间都由坚硬的实体墙阻隔,由一间宫室到达另一间只能通过所驼门王独创的移动方式——传送门。所驼门王为这 N 间藏宝宫室每间都架设了一扇传送门,没有宝藏的宫室不设传送门,所有的宫室传送门分为三种:

1、“横天门”:由该门可以传送到同行的任一宫室;

2、“纵寰门”:由该门可以传送到同列的任一宫室;

3、“自 由 门”:由该门可以传送到以该门所在宫室为中心周围8格中任一宫室(如果目标宫室存在的话)。

深谋远虑的 Henry 当然事先就搞到了所驼门王当年的宫殿招标册,书册上详细记录了每扇传送门所属宫室及类型。而且,虽然宫殿内外相隔,但他自行准备了一种便携式传送门,可将自己传送到殿内任意一间宫室开始寻宝,并在任意一间宫室结束后传送出宫。整座宫殿只许进出一次,且便携门无法进行宫室之间的传送。不过好在宫室内传送门的使用没有次数限制,每间宫室也可以多次出入。

现在 Henry 已经打开了便携门,即将选择一间宫室进入。为得到尽多宝藏, 他希望安排一条路线,使走过的不同藏宝宫室尽可能多。请你告诉 Henry 这条路 线最多行经不同藏宝宫室的数目。

-----------------------------------------------------------------------------------------------------------------------------------------
「输入格式」

第一行给出三个正整数 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,所有的传送门位置互不相同。

「输出格式」

只有一个正整数,表示你确定的路线所经过不同藏宝 宫室的最大数目。

-----------------------------------------------------------------------------------------------------------------------------------------
「样例输入输出」

输入

10 7 7
2 2 1
2 4 2
1 7 2
2 7 3
4 2 2
4 4 1
6 7 3
7 7 1
7 5 2
5 2 1

输出

9

-----------------------------------------------------------------------------------------------------------------------------------------
「数据规模和约定」

测试点编号 N R C
1 16 20 20
2 300 1,000 1,000
3 500 100,000 100,000
4 2,500 5,000 5,000
5 50,000 5,000 5,000
6 50,000 1,000,000 1,000,000
7 80,000 1,000,000 1,000,000
8 100,000 1,000,000 1,000,000
9 100,000 1,000,000 1,000,000
10 100,000 1,000,000 1,000,000

-----------------------------------------------------------------------------------------------------------------------------------------

题解

考虑到 N <= 100,000 矩阵上的点应该很稀疏
于是考虑直接建图做

但是连边的时候将一行中所有“横天门”连在一起 复杂度是 O(N^2)
将一列中所有“纵寰门”连在一起 复杂度也是 O(N^2)

于是考虑将一行中所有“横天门”缩成一个点(将每个“横天门”向该行第一个“横天门”连双向边)
将一列中所有“纵寰门”也缩成一个点(同理)

复杂度就可以降为线性的

再在建好的图上跑 tarjan 把图转为 DAG 再跑 有向无环图的最长路 (dp)

 #include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+;
const int MAXE=1e6+;
int dx[]={,,,,,-,-,-},dy[]={,-,,,-,,,-};
int edge,nex[MAXE],head[MAXN],tail[MAXE];
int redge,rnex[MAXE],rhead[MAXN],rtail[MAXE];
int n,m,r,c;
struct rec{ int x,y,t; } loc[MAXN];
int f[MAXN];
map<int,int> mp[MAXE];
vector<int> a[MAXE],b[MAXE];
int q[MAXN],top=,scc=;
int use[MAXN],cmp[MAXN],low[MAXN],dfn[MAXN],num[MAXN],cnt,ans;
void add(int u,int v){
if (u==v) return;
edge++,nex[edge]=head[u],head[u]=edge,tail[edge]=v;
}
void radd(int u,int v){
redge++,rnex[redge]=rhead[u],rhead[u]=redge,rtail[redge]=v;
}
void clean(){ memset(use,,sizeof(use)); }
void build(){
for (int i=;i<=r;i++){
int x=;
for (int j=;j<a[i].size();j++) if (loc[a[i][j]].t==){ x=a[i][j]; break; }
for (int j=;j<a[i].size();j++){
add(x,a[i][j]);
if (loc[a[i][j]].t==) add(a[i][j],x);
}
}
for (int i=;i<=c;i++){
int x=;
for (int j=;j<b[i].size();j++) if (loc[b[i][j]].t==){ x=b[i][j]; break; }
for (int j=;j<b[i].size();j++){
add(x,b[i][j]);
if (loc[b[i][j]].t==) add(b[i][j],x);
}
}
for (int i=;i<=n;i++){
if (loc[i].t!=) continue;
int x=loc[i].x,y=loc[i].y;
for (int j=;j<;j++){
if (mp[x+dx[j]][y+dy[j]]>) add(i,mp[x+dx[j]][y+dy[j]]);
}
}
}
void tarjan(int u){
low[u]=dfn[u]=++cnt;
q[++top]=u,use[u]=;
for (int e=head[u];e;e=nex[e]){
int v=tail[e];
if (!dfn[v]){
tarjan(v); low[u]=min(low[u],low[v]);
}
else if (use[v]) low[u]=min(low[u],dfn[v]);
}
if (low[u]==dfn[u]){
int x=; scc++;
while (x!=u){
x=q[top--];use[x]=;
cmp[x]=scc;num[scc]++;
}
}
}
void rebuild(){
for (int u=;u<=n;u++){
for (int e=head[u];e;e=nex[e]){
int v=tail[e];
if (cmp[u]!=cmp[v]) radd(cmp[u],cmp[v]);
}
}
}
void dp(int u){
use[u]=;
for (int e=rhead[u];e;e=rnex[e]){
int v=rtail[e];
if (!use[v]) dp(v);
f[u]=max(f[u],f[v]);
}
f[u]+=num[u];
ans=max(ans,f[u]);
}
int main(){
scanf("%d%d%d",&n,&r,&c);
for (int i=;i<=n;i++){
scanf("%d%d%d",&loc[i].x,&loc[i].y,&loc[i].t);
a[loc[i].x].push_back(i);
b[loc[i].y].push_back(i);
mp[loc[i].x][loc[i].y]=i;
}
build();
clean();
for (int i=;i<=n;i++){
if (!dfn[i]) tarjan(i);
}
rebuild();
clean();
for (int i=;i<=scc;i++){
if (!use[i]) dp(i);
}
printf("%d\n",ans);
return ;
}

最新文章

  1. C++的性能C#的产能?! - .Net Native 系列五:.Net Native与反射
  2. 在非SQL客户端使用命令行方式定期连接SQL Server 服务器并模拟用户查询操作,同时输出信息内容
  3. openfire 服务器名称:后面的黄色叹号
  4. EmguCV 阈值化
  5. HDU ACM 1515 Anagrams by Stack
  6. poj3216
  7. HtmlParser的使用-爬虫学习(三)
  8. 我来给.Net设计一款HttpClient
  9. Java_流程控制
  10. vue2.0用组件实现选项卡
  11. PullToRefreshGridView上拉刷新,下拉加载
  12. Go Revel - Validation(验证)
  13. ALGO-5_蓝桥杯_算法训练_最短路
  14. Laravel 教程 - Web 开发实战入门 ( Laravel 5.5 )购买链接
  15. linux下redis4.0.2安装与部署
  16. css左右箭头
  17. linux 基础二---用户群租权限
  18. Moore-Penrose Matrix Inverse 摩尔-彭若斯广义逆 埃尔米特矩阵 Hermitian matrix
  19. go语言web开发框架_Iris框架讲解(五):MVC包使用
  20. 深度解析开发项目之 02 - 使用VTMagic实现左右滑动的列表页

热门文章

  1. [GO]冒泡排序的原理和代码实现
  2. Struts2获取Action中的数据
  3. 编写高质量代码改善C#程序的157个建议——建议35:使用default为泛型类型变量指定初始值
  4. 设计模式11: Flyweight 享元模式(结构型模式)
  5. 关于在审查元素中看到的::before与::after
  6. Http报头中不能添加中文字符
  7. kali linux之端口扫描
  8. c++多线程基础4(条件变量)
  9. Squid系统服务脚本
  10. 跟我一起读postgresql源码(二)——Parser(查询分析模块)