ACM-ICPC 2018 徐州赛区网络预赛 J. Maze Designer

题目:https://nanti.jisuanke.com/t/31462
赛后学长说最大生成树+LCA能过,我思考了下:
1.题目中要求求出最小的建墙方案,那么假设原来的格子和右边下边都有边,这不就是把相对小的边变成墙,反过来就是求最大生成树
2.既然有了最大生成树,那么在这个树上面来一发LCA就ok了
3.点数很大,不能用邻接矩阵存,建边最好就是用kruskal,prim建边有点蛋疼……

#include<cstring>
#include<bits/stdc++.h>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define find fuck
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int MAXN =300010;
const int DEG = 40;
bool vis[MAXN];
int lowc[MAXN];
struct Edge
{
    int to,next;
} edge[MAXN*2];
struct E
{
    int u,v,w;
}e[MAXN*2];
int head[MAXN],tot,tol;
void add(int u,int v,int w)
{
    e[tol].u = u;
    e[tol].v = v;
    e[tol].w = w;
    tol++;
}
void addedge(int u,int v)
{
    edge[tot].to = v;
    edge[tot].next = head[u];
    head[u] = tot++;
}
int f[MAXN];
int find(int v)
{
    return f[v] == v ? v : f[v] = find(f[v]);
}
void init()
{
    tot = 0;
    tol = 0;
    memset(head,-1,sizeof(head));
}
int fa[MAXN][DEG];//fa[i][j]表示结点i的第2^j个祖先
int deg[MAXN];//深度数组
bool flag[MAXN];
int dist[MAXN];
void BFS(int root)
{
    queue<int>que;
    deg[root] = 0;
    fa[root][0] = root;
    dist[root] = 0;
    que.push(root);
    while(!que.empty())
    {
        int tmp = que.front();
        que.pop();
        for(int i = 1; i < DEG; i++)
            fa[tmp][i] = fa[fa[tmp][i-1]][i-1];
        for(int i = head[tmp]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to;
            if(v == fa[tmp][0])
                continue;
            dist[v] = dist[tmp] +1;
            deg[v] = deg[tmp] + 1;
            fa[v][0] = tmp;
            que.push(v);
        }
    }
}
int LCA(int u,int v)
{
    if(deg[u] > deg[v])
        swap(u,v);
    int hu = deg[u], hv = deg[v];
    int tu = u, tv = v;
    for(int det = hv-hu, i = 0; det ; det>>=1, i++)
        if(det&1)
            tv = fa[tv][i];
    if(tu == tv)
        return tu;
    for(int i = DEG-1; i >= 0; i--)
    {
        if(fa[tu][i] == fa[tv][i])
            continue;
        tu = fa[tu][i];
        tv = fa[tv][i];
    }
    return fa[tu][0];
}
int n,m;
int id(int i,int j)
{
    return i*m+j;
}
int main()
{
    while(cin>>n>>m)
    {
        init();
        for(int i = 0; i<n; i++)
        {
            for(int j = 0; j<m; j++)
            {
                f[id(i,j)] = id(i,j);
                char a,b;
                int x,y;
                cin>>a>>x>>b>>y;
                if(a != 'X')
                {
                    add(id(i,j),id(i+1,j),x);
                }
                if(b != 'X')
                {
                    add(id(i,j),id(i,j+1),y);
                }
            }
        }

        sort(e,e+tol,[&](const E &a,const E &b){return a.w > b.w;});
        for(int i = 0;i<tol;i++)
        {
            int u = e[i].u;
            int v = e[i].v;
            int t1 = find(u);
            int t2 = find(v);
            if(t1 != t2)
            {
                f[t1] = t2;
                //cout<<u<<' '<<v<<endl;
                addedge(u,v);
                addedge(v,u);
            }
        }
        BFS(1);
        int q;
        cin>>q;
        while(q--)
        {
            int a,b,c,d;
            cin>>a>>b>>c>>d;
            a--,b--,c--,d--;
            int u = id(a,b);
            int v = id(c,d);
            cout<<dist[u] + dist[v] - 2*dist[LCA(u,v)]<<endl;
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值