CF1326F2 Wise Men (Hard Version) 题解

Description

nn 位智者居住在一座美丽的城市中。他们中的一些人彼此认识。

对于智者的每一种 n!n! 种排列 p1,p2,,pnp_1, p_2, \ldots, p_n,我们可以生成一个长度为 n1n-1 的二进制字符串:对于每个 1i<n1 \leq i < n,如果 pip_ipi+1p_{i+1} 彼此认识,则设 si=1s_i=1,否则 si=0s_i=0

对于所有可能的 2n12^{n-1} 个二进制字符串,求有多少种排列会生成该二进制字符串。

n18n\leq 18

Solution

由于排列不好做,考虑容斥在数列中出现过的数的集合 SS,结束之后做一遍 FWT 即可。

这时会发现枚举 SS 的复杂度已经有 O(2n)O(2^n) 了,再用 dp 算答案的复杂度至少是 O(4nn)O(4^nn),过不了。

考虑对相邻认识的人继续容斥。具体地,我们可以钦定若干个位置 ii,使得 sis_i 必须是 11,那么整个排列就会被划分成若干个连续段,连续段内部必须满足相邻的人互相认识,连续段之间互不影响。

维护 gig_i 表示在 SS 的情况下长度为 ii 的不同连续段数量,直接搜索可以做到 O(4n)O(4^n) 仍然过不了。

但是注意到我们只关心连续段中每个长度出现的次数,而不关心这些长度之间的顺序,所以不同的状态数实际上只有 nn 拆分数 P(n)P(n) 个。

n=18n=18P(n)=385P(n)=385,直接搜出这些状态后再做即可。

时间复杂度:O(2nnP(n))O(2^nnP(n))

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#include <bits/stdc++.h>

#define int int64_t

using u64 = uint64_t;

const int kMaxN = 19, kMaxS = (1 << 18) + 5, kMaxT = 390;

int n, t;
u64 f[kMaxS], ff[kMaxT];
bool gr[kMaxN][kMaxN];
std::vector<int> vec[kMaxT];
std::map<std::vector<int>, int> mp;

void dfs(int x, int len, std::vector<int> &v) {
if (len == n) {
vec[++t] = v, mp[v] = t;
return;
}
if (x + len > n) return;
dfs(x + 1, len, v);
for (int i = 1; len + x * i <= n; ++i) {
v.emplace_back(x);
dfs(x + 1, len + x * i, v);
}
for (; v.size() && v.back() == x; v.pop_back()) {}
}

void getf() {
std::vector<int> vv;
dfs(1, 0, vv);
for (int S = 1; S < (1 << n); ++S) {
static int id[kMaxN];
static u64 g[kMaxN][kMaxN], h[kMaxN];
int t = 0; u64 coef = ((n - __builtin_popcount(S)) & 1) ? -1 : 1;
memset(g, 0, sizeof(g));
for (int i = 1; i <= n; ++i)
if (S >> (i - 1) & 1)
g[1][id[++t] = i] = 1;
for (int i = 2; i <= n; ++i) {
for (int lst = 1; lst <= n; ++lst) {
for (int j = 1; j <= t; ++j)
if (gr[lst][id[j]])
g[i][id[j]] += g[i - 1][lst];
}
}
for (int i = 1; i <= n; ++i) {
h[i] = 0;
for (int j = 1; j <= t; ++j)
h[i] += g[i][id[j]];
// std::cerr << h[i] << ' ';
}
// std::cerr << '\n';
for (int i = 1; i <= ::t; ++i) {
u64 cnt = coef;
for (auto x : vec[i]) cnt *= h[x];
ff[i] += cnt;
// if (i == 3) std::cerr << "fuck " << S << ' ' << cnt << '\n';
}
// std::cerr << __builtin_popcount(S) << ' ' << h[n] << '\n';
// int cnt = coef, len = 0, s = (1 << (n - 1)) - 1;
// std::function<void()> dfs = [&] () {
// if (len == n) return void(f[s] += cnt);
// for (int i = 1; i <= n - len; ++i) {
// if (!h[i]) continue;
// cnt *= h[i], len += i;
// if (len < n) s ^= (1 << (len - 1));
// dfs();
// if (len < n) s ^= (1 << (len - 1));
// // assert(cnt % h[i] == 0);
// cnt /= h[i], len -= i;
// }
// };
// dfs();
}
// std::cerr << ff[3] << '\n';
// std::cerr << t << '\n';
// for (int i = 1; i <= t; ++i) {
// for (auto x : vec[i]) std::cerr << x << ' ';
// std::cerr << '\n';
// }
// std::cerr << "-------------\n";
for (int s = 0; s < (1 << (n - 1)); ++s) {
int lst = 0;
std::vector<int> v;
for (int i = 1; i <= n; ++i) {
if ((~s >> (i - 1) & 1) || i == n) v.emplace_back(i - lst), lst = i;
}
std::sort(v.begin(), v.end());
// std::cerr << mp[v] << '\n';
// for (auto x : v) std::cerr << x << ' ';
// std::cerr << '\n';
f[s] = ff[mp[v]];
}
}

void dickdreamer() {
std::cin >> n;
for (int i = 1; i <= n; ++i) {
std::string str;
std::cin >> str;
for (int j = 1; j <= n; ++j) gr[i][j] = str[j - 1] - '0';
}
getf();
// for (int s = 0; s < (1 << (n - 1)); ++s) std::cerr << f[s] << ' ';
// std::cerr << '\n';
for (int i = 0; i < n - 1; ++i)
for (int s = 0; s < (1 << (n - 1)); ++s)
if (~s >> i & 1)
f[s] -= f[s | (1 << i)];
for (int s = 0; s < (1 << (n - 1)); ++s) std::cout << f[s] << ' ';
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

CF1326F2 Wise Men (Hard Version) 题解
https://sobaliuziao.github.io/2025/09/05/post/27e9cd6.html
作者
Egg_laying_master
发布于
2025年9月5日
许可协议