D - Hangar Hurdles(kruskal重构树+树上倍增)

Problem

  • 给出n*n由.和#组成的网格,q组询问,每组询问给出起点和终点,求每次将箱子从起点推向终点的最大箱子边长(障碍不能在箱子所占范围内)。n<=1000,q<=300000。4s,512M。

Solution

  • 一开始想用floyd预处理,然后O(1)查询,但点的数量级为1e6,会T。
  • 由此题可推出的小结论:当最短路问题的距离定义为路径瓶颈时,答案等价于最值生成树上路径的最值。
  • 预处理求出以每个点为中心的箱子最大边长,一次bfs或者二维前缀和+二分,bfs时从障碍往外bfs。
  • 正解1:kruskal重构树+树上倍增。根据上面的小结论,建立网格中非障碍点的最大生成树,边为两点的最小点权,在此过程中构建kruskal重构树,每次询问两点路径的答案即为重构树上两点的最近公共祖先lca,lca用树上倍增求,总时间复杂度O(qlogn^2)。注意:得到的可能是重构森林。
  • kruskal重构树笔记
  • 正解2:最大生成树+树链剖分+线段树维护区间最值,或者不用线段树和树链剖分,只涉及查询直接在树上倍增。
  • 树上倍增求LCA
  • 树链剖分

Code(有点细节)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
using namespace std;
const int maxn=1005;
int n,node[maxn*maxn*2],bd[maxn],xx[8]={-1,1,0,0,-1,-1,1,1},yy[8]={0,0,-1,1,1,-1,1,-1},cnt,f[maxn*maxn*2],dep[maxn*maxn*2],log[maxn*maxn],last[maxn*maxn*2];
int logg[maxn*maxn],bz[maxn*maxn*2][23],nd,q,vis[maxn][maxn];
char gra[maxn][maxn];
struct edge{
    int u,v,w,next;
}e[maxn*maxn*16];
struct nodee{
    int x,y;
};
bool cmp(const edge &a,const edge &b)
{
    return a.w>b.w;
}
inline void add(int u,int v,int w)
{
    e[++cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=last[u];
    last[u]=cnt;
}
int find(int x)
{
    return x==f[x]?x:f[x]=find(f[x]);
}
void ex_kruskal()
{
    for(int i=1;i<=n*n*2;i++)
        f[i]=i;
    sort(e+1,e+1+cnt,cmp);
    int cnt0=cnt;
    for(int i=1;i<=cnt0;i++)
    {
        int v=e[i].v,u=e[i].u;
        if(find(v)!=find(u))
        {
            int vv=find(v),uu=find(u);
            add(vv,++nd,0);
            add(nd,vv,0);
            add(uu,nd,0);
            add(nd,uu,0);
            f[uu]=f[vv]=nd;
            node[nd]=e[i].w;
        }
    }
}
void makelog()
{
    logg[1]=0;
    for(int i=2;i<=n*n;i++)
        logg[i]=logg[i-1]+((1<<logg[i-1]+1)==i);
}
void dfs(int u,int fa)
{
    for(int i=1;i<=20;i++) bz[u][i]=bz[bz[u][i-1]][i-1];
    for(int i=last[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v==fa) continue;
        dep[v]=dep[u]+1;
        bz[v][0]=u;
        if(v>n*n)
        dfs(v,u);
        else{
            for(int j=1;j<=20;j++)
                bz[v][j]=bz[bz[v][j-1]][j-1];
        }
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    while(dep[x]!=dep[y])
        x=bz[x][logg[dep[x]-dep[y]]];
    if(x==y) return x;
    for(int k=logg[dep[x]];k>=0;k--)
        if(bz[x][k]!=bz[y][k])
        x=bz[x][k],y=bz[y][k];
    return bz[x][0];
}
void bfs()
{
    int inf=1e8;
    queue<nodee>q;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(gra[i][j]=='#')
                q.push({i,j});
            else
                vis[i][j]=inf;
        }
    }
    while(!q.empty())
    {
        nodee nw=q.front();
        q.pop();
        for(int i=0;i<8;i++)
        {
            int nx=nw.x+xx[i],ny=nw.y+yy[i];
            if(nx>=1&&nx<=n&&ny>=1&&ny<=n&&gra[nx][ny]!='#'&&vis[nx][ny]==inf)
            {
                vis[nx][ny]=vis[nw.x][nw.y]+1;
                q.push({nx,ny});
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(gra[i][j]!='#')
            {
                vis[i][j]=2*vis[i][j]-1;
                node[(i-1)*n+j]=min(vis[i][j],2*min(min(i,j),min(n-i+1,n-j+1))-1);
            }
        }
    }
}
int main()
{
    scanf("%d",&n);
    nd=n*n+1;
    for(int i=1;i<=n;i++)
        scanf("%s",gra[i]+1);
    bfs();
    for(int i=1;i<=n*n;i++)
    {
        int x=i/n+1,y=i%n;
        if(!y){x--;y=n;}
        if(gra[x][y]=='#') continue;
        for(int i=0;i<4;i++)
        {
            int nx=x+xx[i],ny=y+yy[i];
            if(nx<=n&&nx>=1&&ny<=n&&ny>=1&&gra[nx][ny]=='.')
            e[++cnt]={n*(x-1)+y,n*(nx-1)+ny,min(node[n*(x-1)+y],node[n*(nx-1)+ny])};
        }
    }
    ex_kruskal();
    makelog();
    for(int i=n*n+1;i<=nd;i++)
    {
        if(find(i)==i)
        {
            dep[i]=1;
            dfs(i,0);
        }
    }
    cin>>q;
    for(int i=1;i<=q;i++)
    {
        int a1,a2,b1,b2;
        scanf("%d%d%d%d",&a1,&a2,&b1,&b2);
        cout<<node[lca((a1-1)*n+a2,(b1-1)*n+b2)]<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哈希表扁豆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值