P4356 [CERC2015] Looping Labyrinth

[题目通道]([CERC2015] Looping Labyrinth - 洛谷)

这条城墙把平面分成三个部分:上面下面 和 里面。不难发现 11 路径如果和 00 路径无交,那就必定完全存在于这三部分之一。不难证明它不可能在里面,要在就在上面或下面,不妨设为上面。

那么 22 路径在 11 路径的上面,33 路径在 22 路径的的上面,……𝜆λ 路径在 𝜆−1λ−1 路径的上面,自然也就在 00 路径的上面。

——等等!𝜆λ 路径难道不就是 00 路径自己吗?!一条路径怎么可能在自己的上面呢?这引出了矛盾,原假设不成立。

有了以上知识储备,我们可以发现,可达的 (∗,∗,0,0)(∗,∗,0,0) 构成的集合的形态是极度有限的。

  • {(0,0,0,0)}{(0,0,0,0)}。最简单的形态。
  • {(𝑢,𝑣,0,0)},gcd⁡(𝑢,𝑣)=1{(u,v,0,0)},gcd(u,v)=1。即找到了一组可达 (𝑢,𝑣,0,0)(u,v,0,0)。
  • 如果找到了两组 (𝑢1,𝑣1,0,0),(𝑢2,𝑣2,0,0)(u1​,v1​,0,0),(u2​,v2​,0,0),那么不难发现任何一个 (𝑥,𝑦,0,0)(x,y,0,0) 都能被表示(具体证明可以见鱼大的博客),即形态为 {(𝑥,𝑦,0,0),𝑥,𝑦∈𝑍}{(x,y,0,0),x,y∈Z}。

考虑求解这个"块可达性形态"。我们从 (0,0,0,0)(0,0,0,0) 开始 BFS,限制每个块内坐标至多访问一次,若试图访问多次便说明我们找到了一组 (𝑢,𝑣)(u,v)。这样复杂度是 𝑂(𝑛𝑚)O(nm) 的,还顺利完成了形态求解的任务。

然后又发现这个形态好像只和 BFS 出的集合有关,换句话说:除了那些根本到不了的点,其他所有点的"块可达性形态"是一样的。直接用相同的做法套就完事了。

最后贴一个代码。。。

#include<bits/stdc++.h>
using namespace std;

int n, m;
bool H[105][105];

struct node {
    int xB, yB, xI, yI;
};
node calc(int x, int y) {
    node ans;
    ans.xB = x >= 0 ? (x / n) : ((x + 1) / n - 1);
    ans.yB = y >= 0 ? (y / m) : ((y + 1) / m - 1);
    ans.xI = x - ans.xB * n; assert(ans.xI >= 0 && ans.xI < n);
    ans.yI = y - ans.yB * m;
    return ans;
}

int U = 0, V = 0;
void insert(int nU, int nV) {
    if (U == -998244353) return; // case 2
    if (nU == 0 && nV == 0) return; // meaningless
    if (nU < 0) nU = -nU, nV = -nV;
    else if (nU == 0 && nV < 0) nV = -nV;
    int g = __gcd(nU, abs(nV)); nU /= g, nV /= g;
    if (nU == U && nV == V) return;
    if (U || V)  U = V = -998244353; // case 1 -> case 2
    else U = nU, V = nV; // case 0 -> case 1
}

bool vis[105][105];
pair<int, int> pB[105][105];
bool isvalid(node u) {
    if (H[u.xI][u.yI]) return 0;
    if (!vis[u.xI][u.yI]) {
        vis[u.xI][u.yI] = 1;
        pB[u.xI][u.yI] = make_pair(u.xB, u.yB);
        return 1;
    }
    insert(u.xB - pB[u.xI][u.yI].first, u.yB - pB[u.xI][u.yI].second);
    return 0;
}

int d[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) {
        char c = getchar();
        while (c != '.' && c != '#') c = getchar();
        for (int j = 0; j < m; j++)
            H[i][j] = c == '#', c = getchar();
    }

    queue<pair<int, int> > Q;
    isvalid((node){0, 0, 0, 0}); Q.push(make_pair(0, 0));
    while (!Q.empty()) {
        pair<int, int> u = Q.front(); Q.pop();
        for (int i = 0; i < 4; i++) {
            int vx = u.first + d[i][0], vy = u.second + d[i][1];
            if (isvalid(calc(vx, vy))) Q.push(make_pair(vx, vy));
        }
    }

    int T; scanf("%d", &T);
    while (T--) {
        int x, y; scanf("%d%d", &x, &y);
        node qaq = calc(x, y);
        if (!vis[qaq.xI][qaq.yI]) { printf("no\n"); continue; }
        if (U == 0 && V == 0) {
            if (qaq.xB != pB[qaq.xI][qaq.yI].first || qaq.yB != pB[qaq.xI][qaq.yI].second)
                printf("no\n");
            else printf("yes\n");
            continue;
        }
        if (U == -998244353 && V == -998244353) {
            printf("yes\n"); continue;
        }
        int nU = qaq.xB - pB[qaq.xI][qaq.yI].first,
            nV = qaq.yB - pB[qaq.xI][qaq.yI].second;
        if (nU == 0 && nV == 0) { printf("yes\n"); continue; }
        if (nU < 0) nU = -nU, nV = -nV;
        else if (nU == 0 && nV < 0) nV = -nV;
        int g = __gcd(nU, abs(nV)); nU /= g, nV /= g;
        if (nU == U && nV == V) { printf("yes\n"); continue; }
        printf("no\n");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值