BZOJ 4242 水壶(BFS建图+最小生成树+树上倍增)

题意

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

思路

码农题。
将每个点以及每个点之间的距离变成边,则此题询问的即为两点((u,v))之间路径的最大边最小是多少。
由图的最小生成树性质,即为该图的最小生成树中((u,v))之间的最大边。所以找到该图的最小生成树之后用树上倍增求解即可。
枚举点对建图是(O(P^2))的,由于有障碍物,经典的(O(PlogP))曼哈顿最小生成树也行不通。
考虑从P个点开始进行一次BFS求出每个点的管辖范围,对于点的管辖范围之间有相邻的那些点,以那些点为中间点建立两个点之间的边,这样就可以把边的数量从(P^2)缩小到(HW)
这样的范围是可以承受的,另外注意判断一下两个点之间不连通的情况。

代码

# include<bits/stdc++.h>
using namespace std;
# define lowbit(x) ((x)&(-x))
# define pi acos(-1.0)
# define eps 1e-8
# define MOD 1000000007
# define INF 1e9
# define mem(a,b) memset(a,b,sizeof(a))
# define FOR(i,a,n) for(register int i=a; i<=n; ++i)
# define FDR(i,a,n) for(register int i=a; i>=n; --i)
# define bug puts("H");
# define lch p<<1,l,mid
# define rch p<<1|1,mid+1,r
# define mp make_pair
# define pb push_back
typedef pair<int,int> PII;
typedef vector<int> VI;
# pragma comment(linker, "/STACK:1024000000,1024000000")
typedef long long LL;
inline char nc(){
    static char buf[1000000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int Scan(){
    char ch=nc();int sum=0, f=1;
    if (ch=='-') f=-1, ch=nc();
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum*f;
}
const int N=2005;
//Code begin....

const int M=200005;
const int DEG=20;
char s[N][N];
struct Node{int x, y;}node[M];
struct Q{int u, v, w;};
struct Edge{int p, next, w;}edge[M<<1];
int head[M], cnt=1, F[M];
int n, m, p, q;
int vis[N][N], id[N][N];
int fa[M][DEG], ma[M][DEG], deg[M];
vector<Q>E;
queue<Node>que;
queue<int>Que;

void add_edge(int u, int v, int w){
    edge[cnt].p=v; edge[cnt].w=w; edge[cnt].next=head[u]; head[u]=cnt++;
}
bool comp(Q a, Q b){return a.w<b.w;}
int find(int x){return F[x]==0?x:(F[x]=find(F[x]));}
void BFS(){
    Node tmp;
    int ps[4][2]={1,0,-1,0,0,1,0,-1};
    mem(vis,-1);
    FOR(i,1,p) {
        vis[node[i].x][node[i].y]=0; id[node[i].x][node[i].y]=i;
        que.push(node[i]);
    }
    while (!que.empty()) {
        tmp=que.front(); que.pop();
        int dx, dy;
        FOR(i,0,3) {
            dx=tmp.x+ps[i][0], dy=tmp.y+ps[i][1];
            if (dx<=0||dx>n||dy<=0||dy>m||s[dx][dy]=='#'||vis[dx][dy]!=-1) continue;
            vis[dx][dy]=vis[tmp.x][tmp.y]+1; id[dx][dy]=id[tmp.x][tmp.y];
            que.push(Node{dx,dy});
        }
    }
}
void Solve(){
    FOR(i,1,n) FOR(j,1,m) {
        if (j!=m&&vis[i][j]!=-1&&vis[i][j+1]!=-1&&id[i][j]!=id[i][j+1]) E.pb(Q{id[i][j],id[i][j+1],vis[i][j]+vis[i][j+1]});
        if (i!=n&&vis[i][j]!=-1&&vis[i+1][j]!=-1&&id[i][j]!=id[i+1][j]) E.pb(Q{id[i][j],id[i+1][j],vis[i][j]+vis[i+1][j]});
    }
    sort(E.begin(),E.end(),comp);
}
void Kruscal(){
    int u, v;
    for (int i=0; i<E.size(); ++i) {
        u=find(E[i].u); v=find(E[i].v);
        if (u!=v) {
            F[u]=v;
            add_edge(E[i].u,E[i].v,E[i].w); add_edge(E[i].v,E[i].u,E[i].w);
        }
    }
}
void bfs(int root){
    deg[root]=0; fa[root][0]=root; ma[root][0]=0; Que.push(root);
    while (!Que.empty()) {
        int tmp=Que.front(); Que.pop();
        FOR(i,1,DEG-1) fa[tmp][i]=fa[fa[tmp][i-1]][i-1], ma[tmp][i]=max(ma[tmp][i-1],ma[fa[tmp][i-1]][i-1]);
        for (int i=head[tmp]; i; i=edge[i].next) {
            int v=edge[i].p;
            if (v==fa[tmp][0]) continue;
            deg[v]=deg[tmp]+1; fa[v][0]=tmp; ma[v][0]=edge[i].w; Que.push(v);
        }
    }
}
int LCA(int u, int v){
    int ans=0;
    if (deg[u]>deg[v]) swap(u,v);
    int hu=deg[u], hv=deg[v], tu=u, tv=v;
    for (int det=hv-hu, i=0; det; det>>=1, ++i) if (det&1) ans=max(ans,ma[tv][i]), tv=fa[tv][i];
    if (tu==tv) return ans;
    for (int i=DEG-1; i>=0; --i) {
        if (fa[tu][i]==fa[tv][i]) continue;
        ans=max(ans,ma[tu][i]); ans=max(ans,ma[tv][i]);
        tu=fa[tu][i]; tv=fa[tv][i];
    }
    return max(ans,max(ma[tu][0],ma[tv][0]));
}
int main ()
{
    int u, v;
    scanf("%d%d%d%d",&n,&m,&p,&q);
    FOR(i,1,n) scanf("%s",s[i]+1);
    FOR(i,1,p) scanf("%d%d",&node[i].x,&node[i].y);
    BFS();
    Solve();
    Kruscal();
    mem(deg,-1);
    FOR(i,1,M) if (deg[i]==-1) bfs(i);
    while (q--) {
        scanf("%d%d",&u,&v);
        if (find(u)!=find(v)) puts("-1");
        else printf("%d
",LCA(u,v));
    }
    return 0;
}
原文地址:https://www.cnblogs.com/lishiyao/p/7621447.html