【bzoj1924】【SDOI2010】所驼门王的宝藏

14 篇文章 0 订阅
2 篇文章 0 订阅

题意

  一个 rc 的矩阵中,有 n 个特殊点,每个特殊点有一个传送门(单向),共三种:
  ①.到达同一行的特殊点(横天门)
  ②.到达同一列的特殊点(纵寰门)
  ③.到达同一九宫格的特殊点(自由门)
  求最多可以到达多少个特殊点

解法

Tarjan缩点+拓扑排序+图上 DP
  和之前的考试的 teach 一题非常相似,不过这一题不能够使用 spfa ,而必须使用 DP
  而为了消除后效性,所以可以按照拓扑序进行 DP
  设 fu 表示到达 u 点(Tarjan缩点之后的编号)能获得的最大点数,那么有:
   fson=maxfsonfu+sizson son u 的孩子,sizson son 的联通块的点数
  另外,本题的建图是一个重点:
  容易想到,如果在第 i 个藏宝宫室可以传送到第j个藏宝宫室,就连一条 i>j 的有向边, Tarjan 强连通分量缩点之后,在新图上找出一条路径,使得这条路径上的所有点对应的强连通分量大小之和最大,这个可以按照拓扑序进行递推来实现。
  但是,在建图上存在一个问题,从横天门或纵寰门引出的边可能特别多。在极端情况下, 105 个横天门在同一行里出现,这样时空复杂度都是无法承受的。考虑这一点进行优化:由于在同一行的横天门一定属于同一个强连通分量,所以在建边时,只需要对在同一行的横天门构建出一个环即可,不需要两两进行连边。而此行内的其他宫室,只需要从这个环中的任意一点向这个宫室连边即可。对于纵寰门也是一样。
  此外,在建图的实现上有一些小技巧,具体见代码。

复杂度

O( knlogn ), k <script type="math/tex" id="MathJax-Element-23">k</script>为常数

70分spfa代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<set>
#include<map>
using namespace std;
const int MAXN=100010;
struct point
{
    int x,y;
    void gi() { scanf("%d%d",&x,&y); }
    bool operator < (const point a) const
    {
        return x==a.x ? y<a.y : x<a.x;
    }
}p[MAXN];
struct node
{
    int next,to;
}t[MAXN*2],e[MAXN*12];
int head[MAXN],Head[MAXN],num;
int type[MAXN],siz[MAXN];
int dfn[MAXN],low[MAXN];
int vis[MAXN],dis[MAXN];
int be[MAXN],s[MAXN];
int cnt,top,tot;
int n,r,c,ans;
int dx[9]={0,1,1,1,0,-1,-1,-1,0};
int dy[9]={0,1,0,-1,-1,-1,0,1,1};
vector<int> H[MAXN*10],L[MAXN*10];
vector<int> N[MAXN*10],M[MAXN*10];
map<int,int> id;
queue<int> q;
set<point> P;
void add(int u,int v)
{
    if( u==v )   return ;
    t[++num]=(node){ head[u],v };
    head[u]=num;
}
void Add(int u,int v)
{
    e[++num]=(node){ Head[u],v };
    Head[u]=num;
}
void Tarjan(int k)
{
    int x;
    vis[k]=1,s[++top]=k;
    dfn[k]=low[k]=++cnt;
    for(int i=head[k]; i ;i=t[i].next)
    {
        x=t[i].to;
        if( !dfn[x] )
            Tarjan( x ),low[k]=min( low[k],low[x] );
        else
            if( vis[x] )   low[k]=min( low[k],dfn[x] );
    }
    if( dfn[k]==low[k] )
    {
        tot++;
        do
        {
            x=s[top--],vis[x]=0;
            be[x]=tot,siz[tot]++;
            for(int i=head[x]; i ;i=t[i].next)   if( !be[t[i].to] || be[t[i].to]!=tot )   Add( tot,t[i].to );
        }while( x!=k );
    }
}
void spfa(int s)
{
    int tmp;
    dis[s]=siz[s];
    q.push( s );
    while( !q.empty() )
    {
        tmp=q.front(),q.pop();
        vis[tmp]=0;
        for(int i=Head[tmp],x; i ;i=e[i].next)
        {
            x=be[e[i].to];
            if( x==tmp )   continue ;
            if( dis[x]<dis[tmp]+siz[x] )
            {
                dis[x]=dis[tmp]+siz[x];
                if( !vis[x] )   vis[x]=1,q.push( x );
            }
        }
    }
}
int main()
{
    set<point>::iterator   l;
    int ex,ey;
    scanf("%d%d%d",&n,&r,&c);
    for(int i=1;i<=n;i++)
    {
        p[i].gi(),scanf("%d",&type[i]);
        if( type[i]==1 )   H[p[i].x].push_back( i ),M[p[i].y].push_back( i );
        if( type[i]==2 )   L[p[i].y].push_back( i ),N[p[i].x].push_back( i );
        if( type[i]==3 )   N[p[i].x].push_back( i ),M[p[i].y].push_back( i );
        id[c*(p[i].x-1)+p[i].y]=i;
        P.insert( p[i] );
    }
    for(int i=1,x;i<=r;i++)
    {
        x=H[i].size();
        if( x<2 )   continue ;
        for(int j=1;j<=x-1;j++)   add( H[i][j-1],H[i][j] );
        add( H[i][x-1],H[i][0] );
    }
    for(int i=1,x;i<=c;i++)
    {
        x=L[i].size();
        if( x<2 )   continue ;
        for(int j=1;j<=x-1;j++)   add( L[i][j-1],L[i][j] );
        add( L[i][x-1],L[i][0] );
    }
    for(int i=1,x;i<=r;i++)
    {
        x=N[i].size()-1;
        if( x<0 || !H[i].size() )   continue ;
        for(int j=0;j<=x;j++)   add( H[i][0],N[i][j] );
    }
    for(int i=1,x;i<=c;i++)
    {
        x=M[i].size()-1;
        if( x<0 || !L[i].size() )   continue ;
        for(int j=0;j<=x;j++)   add( L[i][0],M[i][j] );
    }
    for(int i=1,x;i<=n;i++)
    {
        if( type[i]!=3 )   continue ;
        for(int k=1;k<=8;k++)
        {
            ex=p[i].x+dx[k],ey=p[i].y+dy[k];
            if( ex<1 || ex>r || ey<1 || ey>c )   continue ;
            l=P.find( (point){ ex,ey } );
            if( l==P.end() )   continue ;
            add( i,id[c*(ex-1)+ey] );
        }
    }
    num=0;
    for(int i=1;i<=n;i++)   if( !dfn[i] )   Tarjan( i );
    for(int i=1;i<=tot;i++)   vis[i]=0;
    for(int i=1;i<=tot;i++)   if( !dis[i] )   spfa( i );
    for(int i=1;i<=tot;i++)   ans=max( ans,dis[i] );
    printf("%d\n",ans);
    return 0;
}

100分DP代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<queue>
#include<set>
#include<map>
using namespace std;
const int MAXN=100010;
struct point
{
    int x,y;
    void gi() { scanf("%d%d",&x,&y); }
    bool operator < (const point a) const
    {
        return x==a.x ? y<a.y : x<a.x;
    }
}p[MAXN];
struct node
{
    int next,to;
}t[MAXN*2],e[MAXN*12];
int head[MAXN],Head[MAXN],num;
int dfnm[MAXN],deg[MAXN];
int type[MAXN],siz[MAXN];
int dfn[MAXN],low[MAXN];
int vis[MAXN],dis[MAXN];
int be[MAXN],s[MAXN];
int cnt,top,tot;
int n,r,c,ans;
int dx[9]={0,1,1,1,0,-1,-1,-1,0};
int dy[9]={0,1,0,-1,-1,-1,0,1,1};
vector<int> H[MAXN*10],L[MAXN*10];
vector<int> N[MAXN*10],M[MAXN*10];
map<int,int> id;
queue<int> q;
set<point> P;
void add(int u,int v)
{
    if( u==v )   return ;
    t[++num]=(node){ head[u],v };
    head[u]=num;
}
void Add(int u,int v)
{
    e[++num]=(node){ Head[u],v };
    Head[u]=num;
}
void Tarjan(int k)
{
    int x;
    vis[k]=1,s[++top]=k;
    dfn[k]=low[k]=++cnt;
    for(int i=head[k]; i ;i=t[i].next)
    {
        x=t[i].to;
        if( !dfn[x] )
            Tarjan( x ),low[k]=min( low[k],low[x] );
        else
            if( vis[x] )   low[k]=min( low[k],dfn[x] );
    }
    if( dfn[k]==low[k] )
    {
        tot++;
        do
        {
            x=s[top--],vis[x]=0;
            be[x]=tot,siz[tot]++;
            for(int i=head[x]; i ;i=t[i].next)   if( !be[t[i].to] || be[t[i].to]!=tot )   Add( tot,t[i].to );
        }while( x!=k );
    }
}
void topology()
{
    int tmp;
    for(int i=1;i<=tot;i++)   if( !deg[i] )   q.push( i );
    while( !q.empty() )
    {
        tmp=q.front(),q.pop();
        dfn[tmp]=++cnt,dfnm[cnt]=tmp;
        for(int i=Head[tmp],x; i ;i=e[i].next)
        {
            x=be[e[i].to],deg[x]--;
            if( !deg[x] )   q.push( x );
        }
    }
}
int main()
{
    set<point>::iterator   l;
    int ex,ey;
    scanf("%d%d%d",&n,&r,&c);
    for(int i=1;i<=n;i++)
    {
        p[i].gi(),scanf("%d",&type[i]);
        if( type[i]==1 )   H[p[i].x].push_back( i ),M[p[i].y].push_back( i );
        if( type[i]==2 )   L[p[i].y].push_back( i ),N[p[i].x].push_back( i );
        if( type[i]==3 )   N[p[i].x].push_back( i ),M[p[i].y].push_back( i );
        id[c*(p[i].x-1)+p[i].y]=i;
        P.insert( p[i] );
    }
    for(int i=1,x;i<=r;i++)
    {
        x=H[i].size();
        if( x<2 )   continue ;
        for(int j=1;j<=x-1;j++)   add( H[i][j-1],H[i][j] );
        add( H[i][x-1],H[i][0] );
    }
    for(int i=1,x;i<=c;i++)
    {
        x=L[i].size();
        if( x<2 )   continue ;
        for(int j=1;j<=x-1;j++)   add( L[i][j-1],L[i][j] );
        add( L[i][x-1],L[i][0] );
    }
    for(int i=1,x;i<=r;i++)
    {
        x=N[i].size()-1;
        if( x<0 || !H[i].size() )   continue ;
        for(int j=0;j<=x;j++)   add( H[i][0],N[i][j] );
    }
    for(int i=1,x;i<=c;i++)
    {
        x=M[i].size()-1;
        if( x<0 || !L[i].size() )   continue ;
        for(int j=0;j<=x;j++)   add( L[i][0],M[i][j] );
    }
    for(int i=1;i<=n;i++)
    {
        if( type[i]!=3 )   continue ;
        for(int k=1;k<=8;k++)
        {
            ex=p[i].x+dx[k],ey=p[i].y+dy[k];
            if( ex<1 || ex>r || ey<1 || ey>c )   continue ;
            l=P.find( (point){ ex,ey } );
            if( l==P.end() )   continue ;
            add( i,id[c*(ex-1)+ey] );
        }
    }
    num=0;
    for(int i=1;i<=n;i++)   if( !dfn[i] )   Tarjan( i );
    for(int i=1;i<=tot;i++)   dfn[i]=0;
    cnt=0;
    for(int i=1;i<=tot;i++)
        for(int j=Head[i]; j ;j=e[j].next)   if( be[e[j].to]!=i )   deg[be[e[j].to]]++;
    topology();
    for(int i=1;i<=tot;i++)   dis[i]=siz[i];
    for(int i=1,x;i<=tot;i++)
    {
        x=dfnm[i];
        for(int j=Head[x]; j ;j=e[j].next)
            if( be[e[j].to]!=x )
                dis[be[e[j].to]]=max( dis[be[e[j].to]],dis[x]+siz[be[e[j].to]] );
    }
    for(int i=1;i<=tot;i++)   ans=max( ans,dis[i] );
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值