Maze Designer(最大生成树+LCA)

After the long vacation, the maze designer master has to do his job. A tour company gives him a map which is a rectangle. The map consists of N×MN \times MN×M little squares. That is to say, the height of the rectangle is NNN and the width of the rectangle is MMM. The master knows exactly how the maze is going to use. The tour company will put a couple in two different squares in the maze and make them seek each other. Of course,the master will not make them find each other easily. The only thing the master does is building some wall between some little squares. He knows in that way, wherever the couple is put, there is only one path between them. It is not a difficult thing for him, but he is a considerate man. He also knows that the cost of building every wall between two adjacent squares is different(Nobody knows the reason). As a result, he designs the maze to make the tour company spend the least money to build it.

Now, here's your part. The tour company knows you're the apprentice of the master, so they give you a task. you're given QQQ qustions which contain the information of where the couple will be put. You need to figure out the length of the shortest path between them.

However,the master doesn't tell you how he designs the maze, but he believes that you, the best student of himself, know the way. So he goes on vacation again.

Input

The first line of the input contains two integers NNN and MMM (1≤N,M≤5001 \le N,M \le 5001≤N,M≤500), giving the number of rows and columns of the maze.

The next N×MN \times MN×M lines of the input give the information of every little square in the maze, and their coordinates are in order of (1,1)(1,1)(1,1) , (1,2)(1,2)(1,2) ⋯\cdots⋯ (1,M)(1,M)(1,M) , (2,1)(2,1)(2,1) , (2,2)(2,2)(2,2) , ⋯\cdots⋯ , (2,M)(2,M)(2,M) , ⋯\cdots⋯ ,(N,M)(N,M)(N,M).

Each line contains two characters DDD and RRR and two integers aaa , bbb (0≤a,b≤20000000000 \le a,b \le 20000000000≤a,b≤2000000000 ), aaa is the cost of building the wall between it and its lower adjacent square, and bbb is the cost of building the wall between it and its right adjacent square. If the side is boundary, the lacking path will be replaced with X 000.

The next line contains an integer QQQ (1≤Q≤1000001 \le Q \le 1000001≤Q≤100000 ), which represents the number of questions.

The next QQQ lines gives four integers, x1x_1x1​, y1y_1y1​, x2x_2x2​, y2y_2y2​ ( 1≤x11 \le x_11≤x1​ , x2≤Nx_2 \le Nx2​≤N , 1≤y11 \le y_11≤y1​ , y2≤My_2 \le My2​≤M ), which represent two squares and their coordinate are (x1x_1x1​ , y1y_1y1​) and (x2x_2x2​ , y2y_2y2​).

(xxx,yyy) means row xxx and column yyy.

It is guaranteed that there is only one kind of maze.

Output

For each question, output one line with one integer which represents the length of the shortest path between two given squares.

样例输入

3 3
D 1 R 9
D 7 R 8
D 4 X 0
D 2 R 6
D 12 R 5
D 3 X 0
X 0 R 10
X 0 R 11
X 0 X 0
3
1 1 3 3
1 2 3 2
2 2 3 1

样例输出

4
2
2

题目来源

ACM-ICPC 2018 徐州赛区网络预赛

这道题思路还蛮清晰的。

找出最大生成树,这样就保证了这些墙是不能建的。只建剩下的是最便宜的。

然后把最大生成树中选出来的边连一条权值为1的边,就是重新建边,然后LCA的板子就可以了。

其实写的时候写的好惨。。。。。没想到数组还开小了。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=550000+100;
const int maxm=550000+100;///
int num,head[maxn];
int first[maxn],que[maxn];
int depth[maxn];
int dp[maxn][20],VVis[maxn];
long long dist[maxn];
bool vis[maxn];
int fa[maxn],cnt;
int n,m;
int cntt;
struct node{
    int u;
    int v,next,val;
}G[maxm<<1],GG[maxm<<1];
void init()
{
    cntt=0;
    memset(head,-1,sizeof(head));
  ///  memset(pos,-1,sizeof(pos));
    memset(vis,0,sizeof(vis));
    memset(fa,-1,sizeof(fa));
}
void add(int a,int b,int c){
    G[++cntt].v=b;
    G[cntt].val=c;
    G[cntt].next=head[a];
    head[a]=cntt;
}
int dfs(int u,int deep){
    vis[u]=true;///表示是否访问过
    que[++num]=u;///num在此处相当于dfs_clock
    first[u]=num;///表示第一次走的编号
    depth[num]=deep;

    for(int i=head[u];i!=-1;i=G[i].next){
            int v=G[i].v;
            int w=G[i].val;
        if(!vis[v]){
            dist[v]=dist[u]+w;
            dfs(v,deep+1);
            que[++num]=u;
            depth[num]=deep;
        }
    }
}


void ST(int n)
{
    for(int i=1;i<=n;i++)
        dp[i][0] = i;
    for(int j=1;(1<<j)<=n;j++){
        for(int i=1;i+(1<<j)-1<=n;i++){
            int a = dp[i][j-1] , b = dp[i+(1<<(j-1))][j-1];
            dp[i][j] = depth[a]<depth[b]?a:b;
        }
    }
}
//两个节点之间的路径深度最小的就是LCA
int RMQ(int l,int r)
{
    int k=0;
    while((1<<(k+1))<=r-l+1)
        k++;
    int a = dp[l][k], b = dp[r-(1<<k)+1][k]; //保存的是编号
    return depth[a]<depth[b]?a:b;
}
int LCA(int u ,int v)
{
    int x = first[u] , y = first[v];
    if(x > y) swap(x,y);
    int res = RMQ(x,y);
    return que[res];
}

int findset(int x){return fa[x]==-1?x:fa[x]=findset(fa[x]);}
int cmp(node a,node b){///按照由大到小进行排序
    return a.val>b.val;
}
void kruskal()
{
        int jishu=0;
        sort(GG+1,GG+cnt+1,cmp);///由大到小
        for(int i=1;i<=cnt;i++)
        {
            int u=GG[i].u,v=GG[i].v;

            if( findset(u) != findset(v) )
            {
                add(u,v,1);
                add(v,u,1);
                fa[findset(u)] = findset(v);
                jishu++;
                if(jishu>=n-1)
                    break;
            }
        }
       /// return sum;
}
int xvhao(int i,int j){
    return (i-1)*m+j;
}
int main()
{
    int t,a,b,c;
    char ch1,ch2;
    int x1,x2;
    while(scanf("%d%d",&n,&m)!=EOF){
        init();
        cnt=0;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
               /// scanf("%c%d%c%d",&ch1,&x1,&ch2,&x2);
                cin>>ch1>>x1>>ch2>>x2;

                if(ch1=='D'){
                    GG[++cnt].v=xvhao(i+1,j);
                    GG[cnt].u=xvhao(i,j);
                    GG[cnt].val=x1;
                }
                if(ch2=='R'){
                    GG[++cnt].v=xvhao(i,j+1);
                    GG[cnt].u=xvhao(i,j);
                    GG[cnt].val=x2;
                }
            }
        }
        n=n*m;
        kruskal();
        dfs(1,1);
        ST(2*n-1);
        int q;
        scanf("%d",&q);
        int aa,bb,cc,dd;


        while(q--){
            scanf("%d%d%d%d",&aa,&bb,&cc,&dd);
          ///  cout<<aa<<"*****"<<bb<<endl;
            a=xvhao(aa,bb);
            b=xvhao(cc,dd);
            ///cout<<a<<"   b: "<<b<<endl;
            int lca=LCA(a,b);
          ///  cout<<"lca:"<<lca<<endl;

            printf("%lld\n",dist[a]+dist[b]-2*dist[lca]);
        }
    }
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值