BZOJ 4242: 水壶 Kruskal+BFS

4242: 水壶

Time Limit: 40 Sec  Memory Limit: 256 MB
Submit: 427  Solved: 112
[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

JOI 2013~2014 春季training合宿 竞技2 By PoPoQQQ

 

朴素的暴力:得到任意两点的间的最短距离,然后kruskal。因为是求最大值,所以可以直接用并查集(按秩合并)上的路径最值。树高为O(logP)。共O(P*W*H)

考虑慢慢增加每个建筑物连出去的边权,这个和Kruskal很相似。然后又一个朴素的想法,搞P个队列,同时扩展。碰到一个建筑物才停下来并不扩展这个建筑物,合并这个两个建筑物。O(P*W*H)

既然是同时扩展,那么边权肯定都一样。那么碰到一个已经被扩展的点就可以将距离加起来,用来更新当成这个两个建筑物的距离。边权都*2肯定还是满足kruskal的算法。一个点最多被碰四次。O(W*H*4)

细节:可能边权并没有*2,而是一个奇一个偶,可能会影响同一种边权的kruskal加边顺序,所以我把这些边都存下来......

然后求一发Kruskal就可以了。

#include<cstdio> 
#include<cstring>
#include<algorithm>

const int HW(2010),len(200000),N(4000000);
const int dx[]={0,0,-1,1};
const int dy[]={-1,1,0,0};
int O[HW][HW],last[HW][HW],dis[HW][HW],H,W,P,Q;
struct Palce{int x,y;}q[N+10],now; int l,h;
char ch[HW];int x,y;
struct Node{int a,b,c;}bot[N+10];int top;
int f[len+10],cost[len+10],rank[len+10],depth[len+10];

bool cmp(Node A,Node B){return A.c<B.c;}
int max(int a,int b){return a>b?a:b;}
template <class T>void read(T &x)
{
    x=0;int f=0; char ch=getchar();
    while(ch<'0'||ch>'9'){f=(ch=='-');ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    x=f?-x:x;
}

int gf(int x){return x==f[x]?x:gf(f[x]);}
int gf_d(int x)
{
    int d; for(d=1;x!=f[x];x=f[x])d++;
    return d;
}
void swap(int &a,int &b){if(a==b)return;a^=b;b^=a;a^=b;}
void union_fa(int x,int y,int d)
{
    x=gf(x),y=gf(y); if(x==y)return;
    if(rank[x]>rank[y])   swap(x,y);
    if(rank[x]==rank[y])  rank[y]++;
    f[x]=y; cost[x]=d;
}
void BFS()
{
    h=0;
    while(h<l)
    {
        now=q[++h];
        for(int d=0;d<4;d++)
        if(O[now.x+dx[d]][now.y+dy[d]])
        {
            if(last[now.x+dx[d]][now.y+dy[d]]&&last[now.x+dx[d]][now.y+dy[d]]!=last[now.x][now.y])
                bot[++top]=(Node){last[now.x+dx[d]][now.y+dy[d]],last[now.x][now.y],dis[now.x+dx[d]][now.y+dy[d]]+dis[now.x][now.y]};
            else 
            if(!dis[now.x+dx[d]][now.y+dy[d]])
            {
                dis[now.x+dx[d]][now.y+dy[d]]=dis[now.x][now.y]+1;
                last[now.x+dx[d]][now.y+dy[d]]=last[now.x][now.y];
                q[++l]=(Palce){now.x+dx[d],now.y+dy[d]};
            }
        }
    }
}
int jump(int x,int y)
{
    int d=0;
    if(depth[x]<depth[y])swap(x,y);
    for(;depth[x]!=depth[y];)d=max(d,cost[x]),x=f[x];
    for(;x!=y;)d=max(d,cost[x]),d=max(d,cost[y]),x=f[x],y=f[y];
    return d;
}
int main()
{
//    freopen("C.in","r",stdin);
//    freopen("C.out","w",stdout);    
    read(H),read(W);
    read(P),read(Q);
    for(int i=1;i<=H;i++)
    {
        scanf("%s",ch+1);
        for(int j=1;j<=W;j++)O[i][j]=(ch[j]!='#');
    }
    for(int i=1;i<=P;i++)
    {
        read(x),read(y); f[i]=i; rank[i]=1;
        last[x][y]=i;  q[++l]=(Palce){x,y};
    }
    BFS();//O(H*W)
    std::sort(bot+1,bot+1+top,cmp);
    for(int i=1;i<=top;i++)
    union_fa(bot[i].a,bot[i].b,bot[i].c);//按kruskal走,那么两个连通块之间的过往最大值就是这条边,所以可以直接在并查集的树上求最值。 
    for(int i=1;i<=P;i++)depth[i]=gf_d(i);//O(PlogP)
    for(int i=1;i<=Q;i++)
    {
        read(x);read(y);
        if(gf(x)!=gf(y))printf("-1\n");
        else printf("%d\n",jump(x,y));//O(logP)
    }
    return 0;
}
View Code

 

posted @ 2017-04-09 21:36  Oncle_Ha  阅读(524)  评论(0编辑  收藏  举报