HNOI2015 实验比较

题目链接

题目大意

给定一些数字的大小关系,有>,<和=三种情况。保证输入给定的关系对于每个数来说,<=它的数字最多只有一个。求所有数字有多少种不同的相对大小关系。
n ≤ 100 n\le 100 n100

题解

首先对于等号可以直接缩点,关注到<=某个数字的限制最多只有一个,因此我们从小的向大的连边,形成多棵基环树和树。显然,如果图中有环则无解,因此只剩下来树。为了方便处理,我们新建点0,向每个树的根连边。
接下来显然是树形dp,考虑到序列中可能存在相等的情况,于是令 f [ i ] [ j ] f[i][j] f[i][j]表示dp到点 i i i i i i子树形成的序列中不同数字的个数为 j j j。我们考虑如何合并两棵子树。
同样的,我们把相同的数字看成一个点,那么合成一个序列之后,两个等号不可能相邻地出现。那么就会形成若干对相等的数字,每对中两个数字分别来自两棵子树。剩下的数字用组合数算一下就行了。
我们还需要 h [ i ] [ j ] h[i][j] h[i][j]表示长度为 i i i的序列中画 j j j个不相邻等号的方案数,显然 h [ i ] [ j ] = h [ i − 1 ] [ j ] + h [ i − 2 ] [ j − 1 ] h[i][j]=h[i-1][j]+h[i-2][j-1] h[i][j]=h[i1][j]+h[i2][j1]
于是就可以dp了,复杂度 O ( n 3 ) O(n^3) O(n3)

#include <bits/stdc++.h>
namespace IOStream {
	const int MAXR = 10000000;
	char _READ_[MAXR], _PRINT_[MAXR];
	int _READ_POS_, _PRINT_POS_, _READ_LEN_;
	inline char readc() {
	#ifndef ONLINE_JUDGE
		return getchar();
	#endif
		if (!_READ_POS_) _READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
		char c = _READ_[_READ_POS_++];
		if (_READ_POS_ == MAXR) _READ_POS_ = 0;
		if (_READ_POS_ > _READ_LEN_) return 0;
		return c;
	}
	template<typename T> inline void read(T &x) {
		x = 0; register int flag = 1, c;
		while (((c = readc()) < '0' || c > '9') && c != '-');
		if (c == '-') flag = -1; else x = c - '0';
		while ((c = readc()) >= '0' && c <= '9') x = x * 10 - '0' + c;
		x *= flag;
	}
	template<typename T1, typename ...T2> inline void read(T1 &a, T2&... x) {
		read(a), read(x...);
	}
	inline int reads(char *s) {
		register int len = 0, c;
		while (isspace(c = readc()) || !c);
		s[len++] = c;
		while (!isspace(c = readc()) && c) s[len++] = c;
		s[len] = 0;
		return len;
	}
	inline void ioflush() { fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0; fflush(stdout); }
	inline void printc(char c) {
		if (!c) return;
		_PRINT_[_PRINT_POS_++] = c;
		if (_PRINT_POS_ == MAXR) ioflush();
	}
	inline void prints(const char *s, char c) {
		for (int i = 0; s[i]; i++) printc(s[i]);
		printc(c);
	}
	template<typename T> inline void print(T x, char c = '\n') {
		if (x < 0) printc('-'), x = -x;
		if (x) {
			static char sta[20];
			register int tp = 0;
			for (; x; x /= 10) sta[tp++] = x % 10 + '0';
			while (tp > 0) printc(sta[--tp]);
		} else printc('0');
		printc(c);
	}
	template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
		print(x, ' '), print(y...);
	}
}
using namespace IOStream;
using namespace std;
typedef long long ll;
typedef pair<int, int> P;

const int MAXN = 105, MOD = 1000000007;
struct Edge { int to, next; } edge[MAXN];
int head[MAXN], deg[MAXN], par[MAXN], n, m, tot;
int find(int x) { return x == par[x] ? x : par[x] = find(par[x]); }
inline void addedge(int u, int v) {
	edge[++tot] = (Edge) { v, head[u] };
	head[u] = tot;
}
int mp[MAXN][MAXN], fr[MAXN], to[MAXN], sz[MAXN], que[MAXN];
ll f[MAXN][MAXN], g[MAXN][MAXN], C[MAXN][MAXN], h[MAXN][MAXN];
void dfs(int u) {
	f[u][0] = 1;
	for (int i = head[u]; i; i = edge[i].next) {
		int v = edge[i].to;
		dfs(v);
		for (int j = 0; j <= sz[u]; j++)
		for (int k = 1; k <= sz[v]; k++)
		for (int l = 0; l <= j && l <= k; l++)
			(g[u][j + k - l] += h[j + k][l] % MOD * C[j + k - l - l][j - l] % MOD * f[u][j] % MOD * f[v][k]) %= MOD;
		sz[u] += sz[v];
		for (int j = 0; j <= sz[u]; j++)
			f[u][j] = g[u][j], g[u][j] = 0;
	}
	for (int j = ++sz[u]; j > 0; j--) f[u][j] = f[u][j - 1];
	f[u][0] = 0;
}
int main() {
	read(n, m);
	for (int i = 1; i <= n; i++) par[i] = i;
	int cnt = 0;
	for (int i = 1; i <= m; i++) {
		int a, b; char o[5];
		read(a), reads(o), read(b);
		if (o[0] == '<') ++cnt, fr[cnt] = a, to[cnt] = b;
		else par[find(a)] = find(b);
	}
	for (int i = 1; i <= cnt; i++) {
		int x = find(fr[i]), y = find(to[i]);
		if (x != y && !mp[x][y]) mp[x][y] = 1, addedge(x, y), ++deg[y];
	}
	int he = 0, ta = cnt = 0;
	for (int i = 1; i <= n; i++) if (par[i] == i) {
		if (!deg[i]) addedge(0, i), que[ta++] = i;
		++cnt;
	}
	while (he < ta) {
		int u = que[he++];
		for (int i = head[u]; i; i = edge[i].next) {
			int v = edge[i].to;
			if (!(--deg[v])) que[ta++] = v;
		}
	}
	if (ta != cnt) return puts("0") * 0;
	h[0][0] = C[0][0] = 1;
	for (int i = 1; i <= n; i++) {
		C[i][0] = 1;
		for (int j = 1; j <= i; j++)
			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % MOD;
	}
	for (int i = 1; i <= n; i++)
	for (int j = 0; j * 2 <= i; j++) {
		h[i][j] = h[i - 1][j];
		if (i > 1 && j > 0) h[i][j] = (h[i][j] + h[i - 2][j - 1]) % MOD;
	}
	dfs(0);
	ll res = 0;
	for (int i = 1; i <= n + 1; i++) res += f[0][i];
	printf("%lld\n", res % MOD);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值