Wise Men (Hard Version)
(n) wise men live in a beautiful city. Some of them know each other.
For each of the n! possible permutations (p_1, p_2, ldots, p_n) of the wise men, let's generate a binary string of length (n-1): for each (1 leq i < n) set (s_i=1) if (p_i) and (p_{i+1}) know each other, and (s_i=0) otherwise.
For all possible (2^{n-1}) binary strings, find the number of permutations that produce this binary string.
(2 leq n leq 18)。
题解
For each binary string s, let's calculate (f(s)) – the number of permutations, such that, if (s_i=1), then (p_i) and (p_{i+1}) know each other, otherwise, they may know or don't know each other.
To get real answers, we may use inclusion-exclusion, which may be optimized using a straightforward sum over subsets dp. 说的就是高维前缀差。
To calculate (f(s)), we need to note that (f) depends only on the multiset of lengths of blocks of (1)'s in it.
For example, (f(110111) = f(111011)), because the multiset of lengths is ({1, 3, 4}) (note that block of size (x) of (1)'s corresponds to length (x+1)). 一个 (0) 也对应了一个长度为 (1) 的块。
And note that there are exactly (P(n)) (the number of partitions of (n)) possible multisets.
(P(18) = 385)
To process further, at first let's calculate (g(len, mask)) – the number of paths of length (len), which pass only through the vertices from (mask) (and only through them).
You can calculate it with a straightforward (dp(mask, v)) in (mathcal{O}{(2^n cdot n^2)}).
Then, let's fix the multiset of lengths (a_1, a_2, ldots, a_k).
I claim that the (f(s)) for this multiset is equal to (sum{prod{g(a_i, m_i)}}) over all masks (m_1, m_2, ldots m_k), such that the bitwise OR of (m_1, m_2, ldots, m_k) is equal to (2^n-1) (note that we don't care about the number of bits like in a usual non-intersecting subsets convolution, because if some masks are intersecting, then their OR won't be equal to (2^n-1) because (sum{a_i} = n)).
You can calculate this sum by changing (g(len)) to the sum over subsets. 对 (g(len)) 做高维前缀和。
And then, for this partition, you can just calculate (d(mask)) = (prod{g(a_i, mask)}) in (mathcal{O}{(k cdot 2^n)}), and you can restore the real value of (2^n-1) by inclusion-exclusion in (mathcal{O}{(2^n)}). 注意到 (d(mask)) 统计的方案只保证了经过的点是 (mask) 的子集,所以还要进行一个强制枚举少走了那些点的容斥。
If you will calculate this naively, you will get the (mathcal{O}(( ext{sum of sizes of all partitions}) cdot 2^n)) solution, which is enough to get AC. 如果你枚举出了划分后再暴力算 (d(mask)) 的话就是这个复杂度。
But you can optimize this because you can maintain (d(mask)) during the brute force of all partitions. And in the tree of all partitions, there are (mathcal{O}{(P(n))}) intermediate vertices, so it will work in (mathcal{O}{(P(n) cdot 2^n)}). 在DFS的过程中顺便维护一下 (d(mask)) 就是这个复杂度。
The total complexity is (mathcal{O}{((P(n) + n^2) cdot 2^n))}).
CO int N=18;
int e[N];
int64 dp[1<<N][N],g[N+1][1<<N];
int64 d[1<<N],cur[N+1][1<<N];
int64 f[1<<N];
int main(){
int n=read<int>();
for(int i=0;i<n;++i){
static char s[N];scanf("%s",s);
for(int j=0;j<n;++j)if(s[j]=='1') e[i]|=1<<j;
}
for(int i=0;i<n;++i) dp[1<<i][i]=1;
for(int mask=0;mask<1<<n;++mask)
for(int i=0;i<n;++i)if(dp[mask][i]){
for(int j=0;j<n;++j)if(~mask>>j&1 and e[i]>>j&1)
dp[mask|1<<j][j]+=dp[mask][i];
g[popcount(mask)][mask]+=dp[mask][i];
}
for(int len=1;len<=n;++len){
for(int i=0;i<n;++i)
for(int mask=0;mask<1<<n;++mask)if(mask>>i&1)
g[len][mask]+=g[len][mask^1<<i];
}
map<vector<int>,vector<int> > ok;
for(int mask=0;mask<1<<(n-1);++mask){
int x=0;
vector<int> t;
for(;x<n;++x){
int len=1;
while(mask>>x&1) ++x,++len;
t.push_back(len);
}
sort(t.begin(),t.end());
ok[t].push_back(mask);
}
vector<int> a;
for(int mask=0;mask<1<<n;++mask) d[mask]=1;
function<void(int,int)> dfs=[&](int s,int last){ // sum len, last len
if(s==n){
int64 res=0;
int x=(1<<n)-1;
for(int mask=0;mask<1<<n;++mask){
if(popcount(mask)%2==0) res+=d[x^mask];
else res-=d[x^mask];
}
for(int c:ok[a]) f[c]+=res;
return;
}
if(s+last>n) return;
for(int mask=0;mask<1<<n;++mask) cur[s][mask]=d[mask];
for(int i=last;s+i<=n;++i){ // current len >= last len
if(s+i!=n and s+2*i>n) continue;
a.push_back(i);
for(int mask=0;mask<1<<n;++mask) d[mask]*=g[i][mask];
dfs(s+i,i);
for(int mask=0;mask<(1<<n);++mask) d[mask]=cur[s][mask];
a.pop_back();
}
};
dfs(0,1);
for(int i=0;i<n-1;++i)
for(int mask=0;mask<1<<(n-1);++mask)if(~mask>>i&1)
f[mask]-=f[mask|1<<i];
for(int mask=0;mask<1<<(n-1);++mask)
printf("%lld%c",f[mask],"
"[mask==(1<<(n-1))-1]);
return 0;
}