[bzoj1924][强联通][最长路]所驼门王的宝藏

本文介绍了一个关于在带有特殊传送门的矩阵迷宫中寻找宝藏的问题。通过使用Tarjan算法进行缩点处理,实现对迷宫的有效遍历,进而找到能够访问到的最大数量的不同藏宝宫室。文章详细解释了如何构建迷宫模型、如何利用不同类型传送门的特点,并最终通过计算最短路径得出最优解。

Description

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

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

> 


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

> 


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

> 


深谋远虑的 Henry
当然事先就搞到了所驼门王当年的宫殿招标册,书册上详细记录了每扇传送门所属宫室及类型。而且,虽然宫殿内外相隔,但他自行准备了一种便携式传送门,可将自己传送到殿内任意一间宫室开始寻宝,并在任意一间宫室结束后传送出宫。整座宫殿只许进出一次,且便携门无法进行宫室之间的传送。不过好在宫室内传送门的使用没有次数限制,每间宫室也可以多次出入。
现在 Henry 已经打开了便携门,即将选择一间宫室进入。为得到尽多宝藏, 他希望安排一条路线,使走过的不同藏宝宫室尽可能多。请你告诉
Henry 这条路 线最多行经不同藏宝宫室的数目。

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

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

Sample Input

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

Sample Output

9

HINT

测试点编号 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

题解

其实跟我之前做的一道叫ATM的题差不多
先tarjan缩点一下,一个环的话肯定这个环的宝藏房间可以全部遍历,那就不用理
之后把缩的点拆一下,重新建一下边,拆的点互相连边边权即为联通块节点数
新建st,ed,每个拆出来的2号点向ed连,st向拆出来的1号点连边,权都为0,之后跑最长路即可

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int dx[8]={0,-1,-1,-1,0,1,1,1};
const int dy[8]={-1,-1,0,1,1,1,0,-1};
struct a
{
    int x,y,op,cnt;
}aa[410000],bb[410000];
bool cmpx(a n1,a n2)
{
    if(n1.x!=n2.x)return n1.x<n2.x;
    return n1.y<n2.y;
}
bool cmpy(a n1,a n2){return n1.y<n2.y;}
struct node
{
    int x,y,next;
}a[4110000];int len,last[410000];
void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int belong[411000],dfn[411000],low[411000];
int sta[421000],id,tp,cnt;
bool v[411000];
void dfs(int x)
{
    dfn[x]=low[x]=++id;
    sta[++tp]=x;v[x]=true;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(dfn[y]==-1)
        {
            dfs(y);
            low[x]=min(low[x],low[y]);
        }
        else
        {
            if(v[y])low[x]=min(low[x],dfn[y]);
        }
    }
    if(dfn[x]==low[x])
    {
        int i;
        cnt++;
        do
        {
            i=sta[tp--];
            v[i]=false;belong[i]=cnt;
        }while(i!=x);
    }
}
struct edge
{
    int x,y,c,next;
}e[3110000];int elen,elast[410000];
void eins(int x,int y,int c)
{
    elen++;
    e[elen].x=x;e[elen].y=y;e[elen].c=c;
    e[elen].next=elast[x];elast[x]=elen;
}
int n,R,C;
int fd(int x,int y)
{
    int l=1,r=n;
    while(l<=r)
    {
        int mid=(l+r)/2;
        if(aa[mid].x<x)l=mid+1;
        else if(aa[mid].x>x)r=mid-1;
        else if(aa[mid].y<y)l=mid+1;
        else if(aa[mid].y>y)r=mid-1;
        else return aa[mid].cnt;
    }
    return -1;
}
int h[410000],d[410000];
queue<int> q;
int main()
{
    scanf("%d%d%d",&n,&R,&C);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d%d",&aa[i].x,&aa[i].y,&aa[i].op);
        bb[i].x=aa[i].x;bb[i].y=aa[i].y;bb[i].op=aa[i].op;bb[i].cnt=aa[i].cnt=i;
    }
    sort(aa+1,aa+1+n,cmpx);sort(bb+1,bb+1+n,cmpy);
    len=0;memset(last,0,sizeof(last));
    for(int i=1;i<=n;i++)
    {
        if(aa[i].op==1)
        {
            int u=i-1;
            while(aa[u].x==aa[i].x)ins(aa[i].cnt,aa[u].cnt),u--;
            u=i+1;
            while(aa[u].x==aa[i].x)ins(aa[i].cnt,aa[u].cnt),u++;
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(bb[i].op==2)
        {
            int u=i-1;
            while(bb[u].y==bb[i].y)ins(bb[i].cnt,bb[u].cnt),u--;
            u=i+1;
            while(bb[u].y==bb[i].y)ins(bb[i].cnt,bb[u].cnt),u++;
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(aa[i].op==3)
        {
            for(int k=0;k<=7;k++)
            {
                int x=aa[i].x+dx[k],y=aa[i].y+dy[k];
                if(x<1 || x>R || y<1 || y>C)continue;
                int tp=fd(x,y);
                if(tp!=-1)ins(aa[i].cnt,tp);
            }
        }
    }
    id=cnt=tp=0;
    memset(dfn,-1,sizeof(dfn));
    memset(v,false,sizeof(v));
    memset(low,0,sizeof(low));
    for(int i=1;i<=n;i++)if(dfn[i]==-1)dfs(i);
    for(int i=1;i<=n;i++)h[belong[i]]++;
    elen=0;memset(elast,0,sizeof(elast));
    int st=cnt*2+1,ed=cnt*2+2;
    for(int i=1;i<=cnt;i++)eins(st,i,0),eins(i,i+cnt,h[i]),eins(i+cnt,ed,0);
    for(int i=1;i<=len;i++)
    {
        if(belong[a[i].x]!=belong[a[i].y])eins(belong[a[i].x]+cnt,belong[a[i].y],0);
    }
    for(int i=1;i<=cnt*2+2;i++)d[i]=-999999999;
    d[st]=0;
    q.push(st);
    memset(v,false,sizeof(v));v[st]=true;
    while(!q.empty())
    {
        int x=q.front();q.pop();v[x]=false;
        for(int k=elast[x];k;k=e[k].next)
        {
            int y=e[k].y;
            if(d[y]<d[x]+e[k].c)
            {
                d[y]=d[x]+e[k].c;
                if(v[y]==false)
                {
                    v[y]=true;
                    q.push(y);
                }
            }
        }
    }
    printf("%d\n",d[ed]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值