【HNOI2015】实验比较

题面

题解

首先将所有相等的用并查集缩点,然后会发现题目有一个很有用的性质:

对每张图片\(i\),小D都最多只记住了某一张质量不比\(i\)差的另一张图片\(K_i\)

于是将\(K_i\)作为\(i\)的父亲节点,对于\(K_i = 0\)的点,令\(i\)的父亲为\(n + 1\)即可。

开始树形\(dp\),设\(f[x][i]\)表示\(dp\)到点\(x\),有\(i - 1\)个小于号的方案数。

那么我们可以推出一个式子:

\[f[x][i] = \sum_{to \in son(i), j, k} f'[x][j] \times f[to][k] \times \xi \]

其中\(\xi\)是表示\(j\)段和\(k\)段合并成\(i\)段的方案数。

我们考虑如何求余项\(\xi\),设\(f[x]\)的质量序列为\(A\)\(f'[x]\)的质量序列为\(B\)\(f[to]\)的质量序列为\(C\)

\(A\)的每一段可以只包含\(B\)的一段或者\(C\)的一段,也可以同时包含,但不能为空。特殊地,\(A\)的第一段只能包含节点\(x\)

于是\(\xi\)相当于先枚举\(B\)中的\(j - 1\)段在\(A\)中放的位置,方案数为\(\binom{i - 1}{j - 1}\),然后将\(C\)\(i - j\)段放在\(A\)中剩下的位置,使得每一段都不为空。现在\(C\)中还有\(k - i + j\)段要与\(B\)中的段合并,方案数为\(\binom{j - 1}{k - i + j}\)

于是:

\[\xi = \binom{i - 1}{j - 1} \times \binom{j - 1}{k - i + j} \]

最后答案为\(\sum_{i = 1}^{\mathrm{size}(n + 1)} f[n + 1][i]\)

复杂度:因为每个点对只会在LCA处算\(\mathrm{O}(n)\)次,于是复杂度是\(\mathrm{O}(n^3)\)

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define RG register

inline int read()
{
	int data = 0, w = 1; char ch = getchar();
	while(ch != '-' && (!isdigit(ch))) ch = getchar();
	if(ch == '-') w = -1, ch = getchar();
	while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

inline char get()
{
	char ch = getchar();
	while(ch != '<' && ch != '=') ch = getchar();
	return ch;
}

const int N(105), Mod(1e9 + 7), M(100);
struct edge { int next, to; } e[N << 1];
int n, m, X[N], Y[N], fa[N], eq[N], size[N], e_num;
int belong[N], cnt[N], head[N], C[N][N], f[N][N];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
inline void add_edge(int from, int to)
{
	e[++e_num] = (edge) {head[from], to}; head[from] = e_num;
	e[++e_num] = (edge) {head[to], from}; head[to]   = e_num;
}

inline int merge(int x, int y)
{
	int fx = find(x), fy = find(y);
	fa[fx] = fy; return fx == fy;
}

void dfs(int x, int _f)
{
	static int g[N]; size[x] = f[x][1] = 1;
	for(RG int p = head[x]; p; p = e[p].next)
	{
		int to = e[p].to; if(to == _f) continue;
		dfs(to, x); std::fill(g + 1, g + n + 1, 0);
		for(RG int i = 1; i <= size[x] + size[to]; i++)
			for(RG int j = 1; j <= size[x]; j++)
				for(RG int k = 1; k <= size[to]; k++)
				{
					int t = k - i + j; if(t < 0) continue;
					g[i] = (g[i] + 1ll * f[x][j] * f[to][k] % Mod *
							C[i - 1][j - 1] % Mod * C[j - 1][t]) % Mod;
				}
		size[x] += size[to];
		std::copy(g + 1, g + size[x] + 1, f[x] + 1);
	}
}

int main()
{
	C[0][0] = 1;
	for(RG int i = 1; i <= M; i++)
	{
		C[i][0] = C[i][i] = 1;
		for(RG int j = 1; j < i; j++)
			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % Mod;
	}
	n = read(), m = read();
	for(RG int i = 1; i <= n; i++) fa[i] = i;
	for(RG int i = 1; i <= m; i++)
		X[i] = read(), eq[i] = get() == '=', Y[i] = read();
	for(RG int i = 1; i <= m; i++) if(eq[i]) fa[find(X[i])] = find(Y[i]);
	for(RG int i = 1; i <= n; i++) belong[i] = find(i);
	for(RG int i = 1; i <= n; i++) fa[i] = i;
	for(RG int i = 1; i <= m; i++) if(!eq[i])
	{
		add_edge(belong[X[i]], belong[Y[i]]), ++cnt[belong[Y[i]]];
		if(merge(belong[X[i]], belong[Y[i]])) return puts("0") & 0;
	}
	for(RG int i = 1; i <= n; i++)
		if(belong[i] == i && !cnt[i]) add_edge(n + 1, i);
	int ans = 0; dfs(n + 1, 0);
	for(RG int i = 1; i <= size[n + 1]; i++)
		ans = (ans + f[n + 1][i]) % Mod;
	printf("%d\n", ans);
	return 0;
}
posted @ 2019-02-20 15:33  xgzc  阅读(196)  评论(1编辑  收藏  举报