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;
}
浙公网安备 33010602011771号