Luogu P11808 [PA 2017] 摆砖 / Carcassonne

这题真的是,有点一坨了。

首先考虑转化下这个题目要求的方案。
因为对于不同的定义只针对最终的方案,而对于最终的方案只需要能放出来就行,于是可以转化为,令与 \(\#\) 相邻的 \(.\) 点为黑点,其余 \(.\) 点为白点,要求方案的点构成的每个连通块有至少一个黑点。

对于 \(k = 1\),此时就是黑点个数,记为 \(c\)

对于 \(k = 2\),一个想法是根据是否连通讨论,不过这个做法在后续的扩展性并没有那么强。
一个扩展性强一点的做法是讨论被选中黑点的个数:

  • 如果有 \(2\) 个黑点,那么就是 \(\binom{c}{2}\)
  • 如果有 \(1\) 个黑点,那另一个白点一定与这个黑点相邻,所以可以直接枚举计数。

对于 \(k = 3\),一样分为黑点的个数讨论:

  • 如果有 \(3\) 个黑点,那么就是 \(\binom{c}{3}\)
  • 如果有 \(1\) 个黑点,那么可以枚举黑点后暴力枚举所处的连通块,写个爆搜可以知道只有 \(18\) 种,是可以接受的。
  • 如果有 \(2\) 个黑点,此时白点一定会挨着一个黑点,于是直接枚举这个黑点白点对计数,不过会在白点挨着两个黑点时被多计一次,此时枚举这个白点修正贡献即可。

对于 \(k = 4\),也是一样的:

  • 如果有 \(4\) 个黑点,那么就是 \(\binom{c}{4}\)

  • 如果有 \(1\) 个黑点,那么也是枚举黑点后暴力枚举连通块,方案数只有 \(76\) 种。

  • 如果有 \(3\) 个黑点,那只有 \(1\) 个白点,可以枚举这个黑点白点对,不过依然会在白点挨着两个黑点被多计,这一步容斥后会发现如果白点挨着三个黑点又被少计了,所以需要再修正一下贡献。

  • 如果有 \(2\) 个黑点,这就是整题最难的部分。

    一个想法是按照可能的两种情况(\(1, 3\)\(2, 2\))讨论,不过会发现一个难点是对于一些方案似乎两种情况都可以表示,如果要大力容斥似乎会非常复杂。

    不过会发现只会在这些块都连在一起时才会很麻烦,于是对于连通的情况可以直接枚举像 \(1\) 个黑点一样枚举这个连通块统计。

    那接下来就只需要考虑不连通的情况了,此时再讨论一下可能的情况就比较好做了:

    • \(1, 3\)。那么直接枚举 \(3\) 对应的连通块,剩下的 \(1\) 个的黑点只需要不相邻即可,所以可以统计这个连通块周围的黑点,除掉这些点和连通块本身有的 \(1\) 点就是可以选的。
    • \(2, 2\)。那么还是表示成黑点白点对,可以考虑把这个对表示成 \(1\times 2\)\(2\times 1\) 的矩形,那么就要求选两个存在的矩形,不能有交或相邻。对于一个矩形,不合法的矩形应当是 \(\mathcal{O}(1)\) 的,暴力减掉即可。

时间复杂度 \(\mathcal{O}(n^2)\),不过枚举连通块那里带一个有点大的常数。

写了 10k,为了防止一些越界的东东把下标平移成了 \([3, n + 2]\)

#include <bits/stdc++.h>

using i64 = long long;
constexpr i64 mod = 1e9 + 7;
constexpr i64 inv2 = 500000004, inv3 = 333333336;

inline i64 Cn2(i64 n) { return n * (n - 1) % mod * inv2 % mod; }
inline i64 Cn3(i64 n) { return n * (n - 1) % mod * (n - 2) % mod * inv3 % mod * inv2 % mod; }
inline i64 Cn4(i64 n) { return n * (n - 1) % mod * (n - 2) % mod * (n - 3) % mod * inv3 % mod * inv2 % mod * inv2 % mod * inv2 % mod; }

using info = std::vector<std::pair<int, int> >;

constexpr int maxn = 3e3 + 10;

int n, k;
char s[maxn][maxn];

info now = {{0, 0}};
std::vector<info> vec[5];

int id[maxn][maxn], idn;

void dfs(int step) {
    info tmp = now;
    std::sort(tmp.begin(), tmp.end());
    for (int i = 0; i + 1 < step; i++) {
        if (tmp[i] == tmp[i + 1]) return ;
    }

    tmp.erase(std::find(tmp.begin(), tmp.end(), std::make_pair(0, 0)));
    for (info &oth : vec[step]) {
        if (tmp == oth) return ;
    }
    vec[step].push_back(tmp);

    if (step == 4) return ;

    for (int i = 0; i < step; i++) {
        auto [x, y] = now[i];
        now.emplace_back(x + 1, y), dfs(step + 1), now.pop_back();
        now.emplace_back(x - 1, y), dfs(step + 1), now.pop_back();
        now.emplace_back(x, y + 1), dfs(step + 1), now.pop_back();
        now.emplace_back(x, y - 1), dfs(step + 1), now.pop_back();
    }
}

int cnt1[maxn][maxn];
int h[maxn][maxn], l[maxn][maxn];

int main() {
    scanf("%d%d", &n, &k);
    for (int i = 3; i <= n + 2; i++) scanf("%s", s[i] + 3);
    // range: [3, n + 2]

    for (int i = 0; i <= n + 5; i++) {
        s[0][i] = s[1][i] = s[2][i] = s[n + 3][i] = s[n + 4][i] = s[n + 5][i] =
        s[i][0] = s[i][1] = s[i][2] = s[i][n + 3] = s[i][n + 4] = s[i][n + 5] = '$';
    }

    dfs(1);
    fprintf(stderr, "%zu %zu %zu %zu\n", vec[1].size(), vec[2].size(), vec[3].size(), vec[4].size());

    for (int i = 0; i <= n + 5; i++) {
        for (int j = 0; j <= n + 5; j++) {
            if (s[i][j] != '.') {
                id[i][j] = -1; continue;
            }
            if (s[i + 1][j] == '#' || s[i - 1][j] == '#' || s[i][j + 1] == '#' || s[i][j - 1] == '#') {
                id[i][j] = ++idn;
            }
        }
    }

    if (k == 1) {
        i64 ans = 0;

        for (int i = 3; i <= n + 2; i++) {
            for (int j = 3; j <= n + 2; j++) {
                ans += id[i][j] > 0;
            }
        }

        printf("%lld\n", ans);
        return 0;
    }

    if (k == 2) {
        i64 ans = 0; int cnt = 0;

        for (int i = 3; i <= n + 2; i++) {
            for (int j = 3; j <= n + 2; j++) {
                cnt += id[i][j] > 0;
            }
        }

        ans = Cn2(cnt);

        for (int i = 3; i <= n + 2; i++) {
            for (int j = 3; j <= n + 2; j++) {
                if (id[i][j] <= 0) continue;

                for (const info &f : vec[2]) {
                    int c = 1;
                    for (const auto &[x, y] : f) {
                        if (id[i + x][j + y] != 0) {
                            c = 0; break;
                        }
                    }
                    ans += c;
                }
            }
        }

        printf("%lld\n", ans % mod);
        return 0;
    }

    if (k == 3) {
        i64 ans = 0; int cnt = 0;

        for (int i = 3; i <= n + 2; i++) {
            for (int j = 3; j <= n + 2; j++) {
                cnt += id[i][j] > 0;
            }
        }

        ans = Cn3(cnt);

        for (int i = 3; i <= n + 2; i++) {
            for (int j = 3; j <= n + 2; j++) {
                if (id[i][j] <= 0) continue;

                for (const info &f : vec[2]) {
                    int c = 1;
                    for (const auto &[x, y] : f) {
                        if (id[i + x][j + y] != 0) {
                            c = 0; break;
                        }
                    }
                    ans += c * (cnt - 1);
                }
            }
        }

        ans %= mod;

        for (int i = 3; i <= n + 2; i++) {
            for (int j = 3; j <= n + 2; j++) {
                if (id[i][j] != 0) continue;

                int c = (id[i - 1][j] > 0) + (id[i + 1][j] > 0) + (id[i][j - 1] > 0) + (id[i][j + 1] > 0);
                ans += mod - Cn2(c);
            }
        }

        ans %= mod;

        for (int i = 3; i <= n + 2; i++) {
            for (int j = 3; j <= n + 2; j++) {
                if (id[i][j] <= 0) continue;

                for (const info &f : vec[3]) {
                    int c = 1;
                    for (const auto &[x, y] : f) {
                        if (id[i + x][j + y] != 0) {
                            c = 0; break;
                        }
                    }
                    ans += c;
                }
            }
        }

        printf("%lld\n", ans % mod);
        return 0;
    }


    i64 ans = 0; int cnt = 0;

    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            cnt += id[i][j] > 0;
        }
    }

    ans = Cn4(cnt);

    // cnt black = 4

    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            if (id[i][j] <= 0) continue;

            for (const info &f : vec[2]) {
                int c = 1;
                for (const auto &[x, y] : f) {
                    if (id[i + x][j + y] != 0) {
                        c = 0; break;
                    }
                }
                if (c) ans += Cn2(cnt - 1);
            }
        }
    }

    ans %= mod;

    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            cnt1[i][j] = (id[i - 1][j] > 0) + (id[i + 1][j] > 0) + (id[i][j - 1] > 0) + (id[i][j + 1] > 0);
            if (id[i][j] != 0) continue;

            ans += mod - Cn2(cnt1[i][j]) * (cnt - 2) % mod;
            ans += Cn3(cnt1[i][j]);
        }
    }

    ans %= mod;

    // cnt black = 3

    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            if (id[i][j] <= 0) continue;

            for (const info &f : vec[4]) {
                int c = 1, c1 = 0;
                for (const auto &[x, y] : f) {
                    if (id[i + x][j + y] == -1 || id[i + x][j + y] > id[i][j] || (id[i + x][j + y] && ++c1 > 1)) {
                        c = 0; break;
                    }
                }
                ans += c;
            }
        }
    }

    ans %= mod;

    // cnt black = 1 & 2(connected)

    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            if (id[i][j] <= 0) continue;

            for (const info &f : vec[3]) {
                int c = 1, c1 = cnt - 1 - cnt1[i][j];
                for (const auto &[x, y] : f) {
                    if (id[i + x][j + y] != 0) {
                        c = 0; break;
                    }
                    c1 -= cnt1[i + x][j + y] - (abs(x) + abs(y) == 1);
                }
                if (c) ans += mod + c1;
            }
        }
    }

    ans %= mod;

    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            if (id[i][j] > 0 && id[i][j + 1] > 0) {
                if (id[i + 1][j] == 0 && id[i + 1][j + 1] == 0) {
                    ans += 2;
                }
                if (id[i - 1][j] == 0 && id[i - 1][j + 1] == 0) {
                    ans += 2;
                }
            }
        }
    }

    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            if (id[i][j] > 0 && id[i + 1][j] > 0) {
                if (id[i][j - 1] == 0 && id[i + 1][j - 1] == 0) {
                    ans += 2;
                }
                if (id[i][j + 1] == 0 && id[i + 1][j + 1] == 0) {
                    ans += 2;
                }
            }
        }
    }

    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            if (id[i][j] > 0 && id[i + 1][j + 1] > 0 && id[i + 1][j] == 0 && id[i][j + 1] == 0) {
                ans += 2;
            }
        }
    }
    
    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            if (id[i][j] > 0 && id[i + 1][j - 1] > 0 && id[i + 1][j] == 0 && id[i][j - 1] == 0) {
                ans += 2;
            }
        }
    }

    int tot = 0;
    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            h[i][j] = id[i][j] >= 0 && id[i][j + 1] >= 0 && (id[i][j] == 0) + (id[i][j + 1] == 0) == 1;
            l[i][j] = id[i][j] >= 0 && id[i + 1][j] >= 0 && (id[i][j] == 0) + (id[i + 1][j] == 0) == 1;
            tot += h[i][j] + l[i][j];
        }
    }

    i64 ans1 = 0;
    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            if (! h[i][j]) continue;
            
            ans1 += tot;
            ans1 -= h[i - 1][j - 1] + h[i - 1][j] + h[i - 1][j + 1];
            ans1 -= h[i + 1][j - 1] + h[i + 1][j] + h[i + 1][j + 1];
            ans1 -= h[i][j - 2] + h[i][j - 1] + 1 + h[i][j + 1] + h[i][j + 2];
            ans1 -= l[i - 2][j] + l[i - 2][j + 1] + l[i + 1][j] + l[i + 1][j + 1];
            ans1 -= l[i - 1][j - 1] + l[i - 1][j] + l[i - 1][j + 1] + l[i - 1][j + 2];
            ans1 -= l[i][j - 1] + l[i][j] + l[i][j + 1] + l[i][j + 2];
        }
    }

    for (int i = 3; i <= n + 2; i++) {
        for (int j = 3; j <= n + 2; j++) {
            if (! l[i][j]) continue;
            
            ans1 += tot;
            ans1 -= l[i - 1][j - 1] + l[i][j - 1] + l[i + 1][j - 1];
            ans1 -= l[i - 1][j + 1] + l[i][j + 1] + l[i + 1][j + 1];
            ans1 -= l[i - 2][j] + l[i - 1][j] + 1 + l[i + 1][j] + l[i + 2][j];
            ans1 -= h[i][j - 2] + h[i + 1][j - 2] + h[i][j + 1] + h[i + 1][j + 1];
            ans1 -= h[i - 1][j - 1] + h[i][j - 1] + h[i + 1][j - 1] + h[i + 2][j - 1];
            ans1 -= h[i - 1][j] + h[i][j] + h[i + 1][j] + h[i + 2][j];
        }
    }

    ans1 %= mod, ans1 = ans1 * inv2 % mod, ans += ans1;

    // cnt black = 2(unconnected)

    printf("%lld\n", ans % mod);

    return 0;
}
posted @ 2025-06-01 21:25  rizynvu  阅读(20)  评论(0)    收藏  举报