Description
Solution
注意到(DFS)的时候每次选择一个(DFS)树的子树后必然会走所有子树中的节点,所以原问题变成所有子树内的顺序乘子树外的顺序。
这样可以将还没有经过的节点状压,进行记忆化搜索。(DFS)树的子树个数就是去掉当前点之后的连通块个数,用并查集维护即可。
总答案就是分别将每个节点当做(DFS)树的树根进行一遍记忆化搜索的答案的和。
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
#define ll long long
const int N = 20;
int a[N][N], all, n;
ll dp[N][114514];
class DFSCount
{
public:
long long count(vector<string> G);
};
int Find(int x, int fa[])
{
return fa[x] == x ? x : fa[x] = Find(fa[x], fa);
}
ll Dfs(int x, int zt)
{
if (dp[x][zt]) return dp[x][zt];
if (zt == all) return dp[x][zt] = 1;
int s = 0, cnt = 0, tmp[50], fa[N];
ll f[N];
for (int i = 0; i < n; i++) fa[i] = i;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (!(zt & (1 << i)) && !(zt & (1 << j)) && a[i][j])
fa[Find(i, fa)] = Find(j, fa);
for (int i = 0; i < n; i++)
{
f[i] = 0;
if (!(zt & (1 << i)) && a[x][i]) tmp[++cnt] = i;
if (Find(i, fa) == i && !(zt & (1 << i))) s++;
}
for (int i = 1; i <= cnt; i++)
{
int neww = all, ff = Find(tmp[i], fa);
for (int j = 0; j < n; j++)
if (!(zt & (1 << j)) && Find(j, fa) == ff) neww ^= 1 << j;
neww |= 1 << tmp[i];
f[ff] += Dfs(tmp[i], neww);
}
ll ans = 1;
for (int i = 0; i < n; i++)
if (f[i]) ans *= f[i];
for (int i = 1; i <= s; i++) ans = 1LL * ans * i;
return dp[x][zt] = ans;
}
long long DFSCount::count(vector<string> G)
{
n = G.size(); all = (1 << n) - 1;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (G[i][j] == 'Y')
a[i][j] = 1;
ll ans = 0;
for (int i = 0; i < n; i++) ans += Dfs(i, 1 << i);
return ans;
}