Topcoder SRM713 DFSCount

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;
}
posted @ 2020-07-07 11:38  Tian-Xing  阅读(265)  评论(0编辑  收藏  举报