zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    翻转整数
    无重复字符的最长子串
    字节问题
    django model中get()和filter()方法的区别
    leetcode 290 Word Pattern(map的应用)
    leetcode 130 Surrounded Regions(BFS)
    leetcode 27 Remove Element
    leetcode 102 Binary Tree Level Order Traversal(DFS||BFS)
    leetcode 111 Minimum Depth of Binary Tree(DFS)
    leetcode 104 Maximum Depth of Binary Tree(DFS)
  • 原文地址:https://www.cnblogs.com/lishiyao/p/7621447.html
Copyright © 2011-2022 走看看