[CF1326F] Wise Men (Hard Version)
[题目链接]
https://codeforces.com/contest/1326/problem/F2
[题解]
首先因为有 \(0\) 和 \(1\) 两种关系不好处理。 考虑容斥 , 记 \(ans(s)\) 表示至少有 \(s\) 的方案数。 最后就只需要做一个逆高维前缀和就能求出 \(f(s)\) 了。
考虑设 \(f_{s , i}\) 表示集合 \(s\) 组成一条最后一个节点为 \(i\) 的方案数。 \(g_{s}\) 表示集合 \(s\) 组成一条链的方案数。 \(f\) 可以用简单的状压动态规划求得。 而 \(g_{s}\) 就是 \(\sum{f_{s , i}}\)。
那么有 \(ans_{s} = \sum{\prod_{g_{t_{i}}}}\) , 其中 \(t_{i}\) 表示每个连续段由哪些点组成。 满足 \(t_{1} \bigcup t_{2} \bigcup .... t_{m} = U\) (\(U\) 为全集)。
直接枚举 \(t\) 不可行 , 一个重要性质是 : 对于组成情况相同的两个串 \(s1\) , \(s2\) , 有 \(ans(s1) = ans(s2)\)。
\(18\) 的拆分数为 \(385\) , 并不大 , 于是直接枚举串的组成情况 , 设长度大小关系 \(|t_{1}| \leq |t_{2}| \leq |t_{3}| .... \leq |t_{m}|\) , 在枚举的时候将 \(FMT\) 得到的点值乘起来 , 最后 \(IFMT\) 回去 , 其对这样组成情况的串的贡献就是第 \(2 ^ {n} - 1\) 次项的系数。 具体实现的时候可以不 \(IFMT\) , 直接根据现实意义容斥即可。
时间复杂度 : \(O(2 ^ {N}\rho(n))\) 的。
[代码]
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MN = 20;
#define all(a) (a).begin() , (a).end()
int N;
char s[MN];
int bitcnt[1 << MN];
LL ans[1 << MN] , g[MN][1 << MN] , f[MN][1 << MN];
map < vector < int > , vector < int > > qs;
vector < int > E[MN];
inline void inc(LL &x , LL y) {
x += y;
}
inline void dec(LL &x , LL y) {
x -= y;
}
inline void dfs(int r , const std::vector<int> &l, const std::vector<LL> &s) {
if (!r) {
LL res = 0; int msk = (1 << N) - 1;
for (int i = 0; i < (1 << N); ++i)
if (bitcnt[msk ^ i] & 1) dec(res , s[i]);
else inc(res , s[i]);
for (int o : qs[l]) inc(ans[o] , res);
return;
}
int lst = l.empty() ? 1 : l.back();
for (int k = lst; k <= r; ++k) {
if (k < r && r - k < k) continue;
vector < LL > ns(s); vector < int > nl(l);
nl.emplace_back(k);
for (int i = 0; i < (1 << N); ++i) ns[i] *= g[k - 1][i];
dfs(r - k , nl , ns);
}
}
int main() {
scanf("%d" , &N);
for (int i = 0; i < N; ++i) {
scanf("%s" , s);
for (int j = 0; j < N; ++j)
if (s[j] == '1') E[i].emplace_back(j);
}
bitcnt[0] = 0;
for (int i = 1; i < (1 << N); ++i) bitcnt[i] = bitcnt[i >> 1] + (i & 1);
for (int i = 0; i < N; ++i) f[i][1 << i] = 1;
for (int S = 1; S < (1 << N); ++S)
for (int i = 0; i < N; ++i)
if (f[i][S]) {
inc(g[bitcnt[S] - 1][S] , f[i][S]);
for (int v : E[i]) if ((S >> v & 1) == 0) inc(f[v][S | (1 << v)] , f[i][S]);
}
for (int k = 0; k < N; ++k)
for (int i = 0; i < N; ++i)
for (int S = 0; S < (1 << N); ++S)
if (S >> i & 1) inc(g[k][S] , g[k][S ^ (1 << i)]);
for (int S = 0; S < (1 << (N - 1)); ++S) {
vector < int > a; int x = 1;
for (int i = 0; i < N - 1; ++i)
if (S >> i & 1) ++x;
else a.emplace_back(x) , x = 1;
a.emplace_back(x); sort(all(a));
qs[a].emplace_back(S);
}
dfs(N , vector < int > () , vector< LL > (1 << N , 1));
for (int i = 0; i < N - 1; ++i)
for (int S = 0; S < (1 << (N - 1)); ++S)
if (S >> i & 1) dec(ans[S ^ (1 << i)] , ans[S]);
for (int S = 0; S < (1 << (N - 1)); ++S) printf("%lld " , ans[S]);
printf("\n");
return 0;
}