BZOJ4242:水壶(BFS & 最小生成树)

4242: 水壶

Time Limit: 40 Sec   Memory Limit: 512 MB
Submit: 946   Solved: 241
[ Submit][ Status][ Discuss]

Description

JOI君所居住的IOI市以一年四季都十分炎热著称。
IOI市是一个被分成纵H*横W块区域的长方形,每个区域都是建筑物、原野、墙壁之一。建筑物的区域有P个,编号为1...P。
JOI君只能进入建筑物与原野,而且每次只能走到相邻的区域中,且不能移动到市外。
JOI君因为各种各样的事情,必须在各个建筑物之间往返。虽然建筑物中的冷气设备非常好,但原野上的日光十分强烈,因此在原野上每走过一个区域都需要1单位的水。此外,原野上没有诸如自动售货机、饮水处之类的东西,因此IOI市的市民一般都携带水壶出行。大小为x的水壶最多可以装x单位的水,建筑物里有自来水可以将水壶装满。
由于携带大水壶是一件很困难的事情,因此JOI君决定携带尽量小的水壶移动。因此,为了随时能在建筑物之间移动,请你帮他写一个程序来计算最少需要多大的水壶。
现在给出IOI市的地图和Q个询问,第i个询问(1<=i<=Q)为“在建筑物Si和Ti之间移动,最小需要多大的水壶?”,请你对于每个询问输出对应的答案。

Input

第一行四个空格分隔的整数H,W,P,Q,表示IOI市被分成了纵H*横W块区域,有P个建筑物,Q次询问。
接下来H行,第i行(1<=i<=H)有一个长度为W的字符串,每个字符都是’.’或’#’之一,’.’表示这个位置是建筑物或原野,’#’表示这个位置是墙壁。
接下来P行描述IOI市每个建筑物的位置,第i行(1<=i<=P)有两个空格分隔的整数Ai和Bi,表示第i个建筑物的位置在第Ai行第Bi列。保证这个位置在地图中是’.’
接下来Q行,第i行(1<=i<=Q)有两个空格分隔的整数Si和Ti,表示第i个询问为“在建筑物Si和Ti之间移动,最小需要多大的水壶?”

Output

输出Q行,第i行(1<=i<=Q)一个整数,表示在建筑物Si和Ti之间移动最小需要多大的水壶。如果无法到达,输出-1。此外,如果不需要经过原野就能到达,输出0。

Sample Input

5 5 4 4
.....
..##.
.#...
..#..
.....
1 1
4 2
3 3
2 5
1 2
2 4
1 3
3 4

Sample Output

3
4
4
2

HINT

1<=H<=2000

1<=W<=2000

2<=P<=2*10^5

1<=Q<=2*10^5

1<=Ai<=H(1<=i<=P)

1<=Bi<=W(1<=i<=P)

(Ai,Bi)≠(Aj,Bj)(1<=i<j<=P)

1<=Si<Ti<=P(1<=i<=Q)

Source


题意:略。

思路:首先看一道题:一个无向图,表示询问从A点走到B点的所有路径中,最长的边最小值是多少?经过“简单”的证明,“显然”该边在最小生成树上,那么我们把最小生成树建出来,倍增维护一下点间的最大边即可。本题也是一样,不过需要把建筑物的最小生成树求出来,具体做法是从每个建筑物开始BFS,两个建筑物的“势力范围”相遇时就作为一条边先存储着,最后扔到kruskal即可,那么如果建筑物A和C进行BFS时被B的“势力范围“截住了怎么办,显然AB+BC边比AC边更优,AC不要都行。拓展到图上面如BZOJ4144留坑,那题用dijk建边。

# include <iostream>
# include <cstdio>
# include <cstring>
# include <queue>
# define pb push_back
# define mp make_pair
# define pii pair<int,int>
using namespace std;
const int maxn = 2e3+13;
const int maxm = 2e5+13;
int dx[] = {0,1,0,-1}, dy[]={1,0,-1,0};
int n, m, p, q;
int fa[maxm], dep[maxm], Fa[maxm][22], dis[maxm][22];
char s[maxn][maxn];
pii sta[maxn][maxn];
vector<pii >v[maxn*maxn],g[maxm];
queue<pii >Q;
void bfs()
{
    while(!Q.empty())
    {
        int x = Q.front().first, y = Q.front().second;
        Q.pop();
        for(int i=0; i<4; ++i)
        {
            int nx = x+dx[i];
            int ny = y+dy[i];
            if(nx<1||ny<1||nx>n||ny>m||s[nx][ny]=='#') continue;
            if(sta[nx][ny] == mp(0,0))
                sta[nx][ny] = mp(sta[x][y].first+1, sta[x][y].second), Q.push(mp(nx,ny));
            else if(sta[nx][ny].second != sta[x][y].second)
                v[sta[nx][ny].first+sta[x][y].first].pb(mp(sta[nx][ny].second,sta[x][y].second));
        }
    }
}
int find(int x) {return x==fa[x]?x:fa[x]=find(fa[x]);}
void merge_build()
{
    int cnt = 0;
    for(int i=1; i<=p; ++i) fa[i] = i;
    for(int i=0; i<=n*m&&cnt<p-1; ++i)
    {
        for(int j=0; j<v[i].size()&&cnt<p-1; ++j)
        {
            int fu = find(v[i][j].first), fv = find(v[i][j].second);
            if(fu != fv)
            {
                fa[fu] = fv;
                ++cnt;
                g[v[i][j].first].pb(mp(v[i][j].second, i));
                g[v[i][j].second].pb(mp(v[i][j].first, i));
            }
        }
    }
}
void dfs(int cur, int pre)
{
    Fa[cur][0] = pre;
    dep[cur] = dep[pre] + 1;
    for (int i=1; i<=20; ++i)
    {
        Fa[cur][i]=Fa[Fa[cur][i-1]][i-1];
        dis[cur][i]=max(dis[cur][i-1],dis[Fa[cur][i-1]][i-1]);
    }
    for(int i=0; i<g[cur].size(); ++i)
    {
        int to = g[cur][i].first, cost = g[cur][i].second;
        if(to != pre)
        {
            dis[to][0] = cost;
            dfs(to, cur);
        }
    }
}

int query(int u, int v)
{
    if(find(u) != find(v)) return -1;
    int res = 0;
    if (dep[u]<dep[v]) swap(u,v);
    for (int k=20;~k;--k)
        if (((dep[u]-dep[v])>>k)&1)
            res=max(res,dis[u][k]),u=Fa[u][k];
    if (u==v) return res;
    for (int k=20;~k;--k)
        if (Fa[u][k]!=Fa[v][k])
        {
            res=max(res,dis[u][k]);
            res=max(res,dis[v][k]);
            u=Fa[u][k],v=Fa[v][k];
        }
    return max(res,max(dis[v][0],dis[u][0]));
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&p,&q);
    for(int i=1; i<=n; ++i)
        scanf("%s",s[i]+1);
    for(int i=1; i<=p; ++i)
    {
        int x, y;
        scanf("%d%d",&x,&y);
        sta[x][y] = mp(0, i);
        Q.push(mp(x,y));
    }
    bfs();
    merge_build();
    for(int i=1; i<=p; ++i)
        if(!dep[i])
            dfs(i, 0);
    while(q--)
    {
        int x, y;
        scanf("%d%d",&x,&y);
        printf("%d\n",query(x,y));
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值