Description
有 n 位智者居住在一座美丽的城市中。他们中的一些人彼此认识。
对于智者的每一种 n! 种排列 p1,p2,…,pn,我们可以生成一个长度为 n−1 的二进制字符串:对于每个 1≤i<n,如果 pi 和 pi+1 彼此认识,则设 si=1,否则 si=0。
对于所有可能的 2n−1 个二进制字符串,求有多少种排列会生成该二进制字符串。
n≤18。
Solution
由于排列不好做,考虑容斥在数列中出现过的数的集合 S,结束之后做一遍 FWT 即可。
这时会发现枚举 S 的复杂度已经有 O(2n) 了,再用 dp 算答案的复杂度至少是 O(4nn),过不了。
考虑对相邻认识的人继续容斥。具体地,我们可以钦定若干个位置 i,使得 si 必须是 1,那么整个排列就会被划分成若干个连续段,连续段内部必须满足相邻的人互相认识,连续段之间互不影响。
维护 gi 表示在 S 的情况下长度为 i 的不同连续段数量,直接搜索可以做到 O(4n) 仍然过不了。
但是注意到我们只关心连续段中每个长度出现的次数,而不关心这些长度之间的顺序,所以不同的状态数实际上只有 n 拆分数 P(n) 个。
n=18 时 P(n)=385,直接搜出这些状态后再做即可。
时间复杂度:O(2nnP(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]]; } for (int i = 1; i <= ::t; ++i) { u64 cnt = coef; for (auto x : vec[i]) cnt *= h[x]; ff[i] += cnt; } } 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()); 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 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; while (T--) dickdreamer(); return 0; }
|