[CERC2016]机棚障碍 Hangar Hurdles

传送门:2016-2017 ACM-ICPC, Central Europe Regional Contest (CERC 16)

2016-2017年中欧地区的比赛题,不得不说算是冷门了...

题面大意:

​ 给定\(n*m\)网格图,某些位置为·障碍,多组询问,询问从给定位置到达给定位置间可以推过的最大箱子。

​ 题目里有些需要注意的,“从某点”意味着这个点作为箱子的中心点,那么显然箱子的大小只能为奇数。

分析:

​ 可以说是典型的缝合怪了......,是口头\(AC\)五分钟实际处理两小时的类型。我们考虑将每个位置可通过的最大箱子,然后建图,构造最大生成树,询问时就先判断是否在同一生成树中,若在同一生成树中就用\(LCA\)维护快速求出两点间路径的最小值。

​ 首先考虑如何处理出每个位置可通过的最大箱子,前缀和+二分即可。

bool check(int x, int y, int z)
{
    if (x + z > n || y + z > n || x - z < 1 || y - z < 1)
        return false;
    if (f[x + z][y + z] - f[x + z][y - z - 1] - f[x - z - 1][y + z] + f[x - z - 1][y - z - 1] == (2 * z + 1) * (2 * z + 1))
        return true;
    return false;
}
int work(int x, int y)
{
    int l = 0, r = n, ans;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (check(x, y, mid))
        {
            ans = mid;
            l = mid + 1;
        }
        else
        {
            r = mid - 1;
        }
    }
    return ans;
}
for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= n; ++j)
        {
            if (a[i][j] == '.')
            {
                qaq++;
            }
            f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + (a[i][j] == '.');
        }
    }
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= n; ++j)
        {
            if (a[i][j] == '.')
            {
                qwq[i][j] = 2 * work(i, j) + 1;
            }
        }
    }

​ 然后就是建图和最大生成树,唯一可能需要注意的就是\(LCA\)的维护,具体可以看\(NOIP\)某年的货车运输,两者几乎相同。

void dfs(int x, int father, int col, int z)
{
    vis[x] = col;
    dep[x] = dep[father] + 1;
    F[x][0] = father;
    dis[x][0] = z;
    for (int i = 1; i <= 20; ++i)
    {
        F[x][i] = F[F[x][i - 1]][i - 1];
        dis[x][i] = min(dis[x][i - 1], dis[F[x][i - 1]][i - 1]);
    }
    for (auto u : G[x])
    {
        if (u.first != father)
        {
            dfs(u.first, x, col, u.second);
        }
    }
}
int lca(int x, int y)
{
    int ans = 1005*1005;
    if (dep[x] < dep[y])
        swap(x, y);
    for (int i = 20; i >= 0; --i)
    {
        if (dep[F[x][i]] >= dep[y])
        {
            ans = min(ans, dis[x][i]);
            x = F[x][i];
        }
    }
    if (x == y)
    {
        return ans;
    }
    for (int i = 20; i >= 0; --i)
    {
        if (F[x][i] != F[y][i])
        {
            ans = min(ans, min(dis[x][i], dis[y][i]));
            x = F[x][i], y = F[y][i];
        }
    }
    ans = min(ans, min(dis[x][0], dis[y][0]));
    return ans;
}

​ 理论上来讲缩点和树剖会让代码处理速度更快,但这样代码长度可能达到\(300\)行往上...所以就写了常数更大更容易写的倍增求\(LCA\)

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <string.h>
#include <cmath>
#include <deque>
#include <vector>
#define Lint long long
using namespace std;
typedef pair<int, int> P;
int n, q, m, qaq;
char a[1005][1005];
int f[1005][1005];
int qwq[1005][1005];
int F[1005*1005][21], dis[1005*1005][21];
int vis[1005 * 1005];
int dep[1005 * 1005];
vector<P> G[1005 * 1005];
struct node
{
    int x, y, z;
} e[1005 * 1005 * 2];
int fa[1005 * 1005], sumcol;
bool cmp(node x, node y) { return x.z > y.z; }
bool check(int x, int y, int z)
{
    if (x + z > n || y + z > n || x - z < 1 || y - z < 1)
        return false;
    if (f[x + z][y + z] - f[x + z][y - z - 1] - f[x - z - 1][y + z] + f[x - z - 1][y - z - 1] == (2 * z + 1) * (2 * z + 1))
        return true;
    return false;
}
int getid(int x, int y) { return (x - 1) * n + y; }
int Find(int x)
{
    if (x == fa[x])
        return x;
    return fa[x] = Find(fa[x]);
}
int work(int x, int y)
{
    int l = 0, r = n, ans;
    while (l <= r)
    {
        int mid = (l + r) >> 1;
        if (check(x, y, mid))
        {
            ans = mid;
            l = mid + 1;
        }
        else
        {
            r = mid - 1;
        }
    }
    return ans;
}
void krsual()
{
    for (int i = 1; i <= m; ++i)
    {
        int le = Find(e[i].x), re = Find(e[i].y);
        if (fa[le] != re)
        {
            fa[le] = re;
            G[e[i].x].push_back({e[i].y, e[i].z});
            G[e[i].y].push_back({e[i].x, e[i].z});
        }
    }
}
void dfs(int x, int father, int col, int z)
{
    vis[x] = col;
    dep[x] = dep[father] + 1;
    F[x][0] = father;
    dis[x][0] = z;
    for (int i = 1; i <= 20; ++i)
    {
        F[x][i] = F[F[x][i - 1]][i - 1];
        dis[x][i] = min(dis[x][i - 1], dis[F[x][i - 1]][i - 1]);
    }
    for (auto u : G[x])
    {
        if (u.first != father)
        {
            dfs(u.first, x, col, u.second);
        }
    }
}
int lca(int x, int y)
{
    int ans = 1005*1005;
    if (dep[x] < dep[y])
        swap(x, y);
    for (int i = 20; i >= 0; --i)
    {
        if (dep[F[x][i]] >= dep[y])
        {
            ans = min(ans, dis[x][i]);
            x = F[x][i];
        }
    }
    if (x == y)
    {
        return ans;
    }
    for (int i = 20; i >= 0; --i)
    {
        if (F[x][i] != F[y][i])
        {
            ans = min(ans, min(dis[x][i], dis[y][i]));
            x = F[x][i], y = F[y][i];
        }
    }
    ans = min(ans, min(dis[x][0], dis[y][0]));
    return ans;
}
int main()
{
    memset(dis, 0x3f, sizeof(dis));
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
    {
        scanf("%s", a[i] + 1);
    }
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= n; ++j)
        {
            if (a[i][j] == '.')
            {
                qaq++;
            }
            f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1] + (a[i][j] == '.');
        }
    }
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= n; ++j)
        {
            if (a[i][j] == '.')
            {
                qwq[i][j] = 2 * work(i, j) + 1;
            }
        }
    }
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= n; ++j)
        {
            if (a[i][j] == '.')
            {
                if (i <= n - 1)
                {
                    if (a[i + 1][j] == '.')
                    {
                        e[++m].x = getid(i, j);
                        e[m].y = getid(i + 1, j);
                        e[m].z = min(qwq[i][j], qwq[i + 1][j]);
                    }
                }
                if (j <= n - 1)
                {
                    if (a[i][j + 1] == '.')
                    {
                        e[++m].x = getid(i, j);
                        e[m].y = getid(i, j + 1);
                        e[m].z = min(qwq[i][j], qwq[i][j + 1]);
                    }
                }
            }
        }
    }
    sort(e + 1, e + 1 + m, cmp);
    for (int i = 1; i <= n * n; ++i)
        fa[i] = i;
    krsual();
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= n; ++j)
        {
            if (a[i][j] == '.')
            {
                if (!vis[getid(i, j)])
                {
                    dfs(getid(i, j), 0, ++sumcol, 0);
                }
            }
        }
    }
    // for(int i=0;i<=20;++i){
    //     cout<<F[getid(2,2)][i]<<" "<<dis[getid(2,2)][i]<<endl;
    // }
    scanf("%d", &q);
    int bex, bey, rex, rey;
    while (q--)
    {
        scanf("%d%d%d%d", &bex, &bey, &rex, &rey);
        if (vis[getid(bex, bey)] != vis[getid(rex, rey)])
        {
            puts("0");
        }
        else
        {
            printf("%d\n", lca(getid(bex, bey), getid(rex, rey)));
        }
    }
    return 0;
}