【bzoj4013】 HNOI2015—实验比较

http://www.lydsy.com/JudgeOnline/problem.php?id=4013 (题目链接)

题意

  给出$n$个数的$m$个大小关系,问它们之间可以形成的单调不降的序列有多少种。

Solution

  首先,因为等于号相连的两个数位置互换不会产生新的方案,我们先用并查集把用等号相连的点全部缩成一个。如果此时的图中出现了环,那么答案为$0$。考虑答案不为$0$的情况怎么处理。此时的图已经成为了一个DAG,我们需要在上面统计方案。容易发现,对于一个点,有分有合,合的情况很好处理,分的情况就很尴尬了,什么?你说每一个点只有一条入边?(哔了狗了)。因为并查集缩点后整个图已经变成了一棵树,我们考虑如何进行树形dp。

  $f[x][i]$表示在$x$的子树中,组成的序列用$<$相连的等价类个数为$i$个的序列方案,其中等价类就表示由等号相连的一坨数。不妨设$y$是$x$的某个儿子,那么转移:

\begin{aligned}  g[i+l]=f[x][i]*f[y][j]*\binom{i-1+l}{j-1}*\binom{j-1}{k-l}   \end{aligned}

  其中$i\in[1,size[x]]$,$j\in[1,size[y]]$,$l\in[max(0,k-j+1),k]$。$g$是一个临时的存储答案的数组。$l$是我们枚举的$y$所贡献的等价类,那么剩下的$k-l$就是$y$中与原本$x$中相等的数的个数。$x$永远排在序列首位而且不会与任意一个数相等。$\binom{i-1+l}{j-1}$表示两个无相对关系的已经排序好的序列合并为一个序列的方案。$\binom{j-1}{k-l}$表示在$x$的$j-1$个数中选出与$y$的$k-l$个数相等的数的方案。

细节

  可能是个森林,所以用一个超级源点连向若干根节点。

代码

// bzoj4013
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define LL long long
#define inf (1ll<<30)
#define MOD 1000000007
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;

const int maxn=200;
LL C[maxn][maxn],f[maxn][maxn],g[maxn];
int head[maxn],size[maxn],vis[maxn],fa[maxn],r[maxn],n,m,c,cnt,sum;
struct data {int u,v;}a[maxn];
struct edge {int to,next;}e[maxn];

int find(int x) {
	return x==fa[x] ? x : fa[x]=find(fa[x]);
}
void link(int u,int v) {
	e[++cnt]=(edge){v,head[u]};head[u]=cnt;
}
bool bfs() {
	queue<int> q;q.push(0);
	int tot=0;
	while (!q.empty()) {
		int x=q.front();q.pop();tot++;
		for (int i=head[x];i;i=e[i].next)
			if (!--r[e[i].to]) q.push(e[i].to);
	}
	return tot==sum+1;
}
void dfs(int x) {
	vis[x]=f[x][1]=size[x]=1;
	for (int i=head[x];i;i=e[i].next) if (!vis[e[i].to]) {
			int y=e[i].to;dfs(y);
			for (int j=1;j<=size[x];j++) {
				if (!f[x][j]) continue;
				for (int k=1;k<=size[y];k++) {
					if (!f[y][k]) continue;
					for (int l=max(0,k-j+1);l<=k;l++)
						(g[j+l]+=f[x][j]*f[y][k]%MOD*C[j+l-1][j-1]%MOD*C[j-1][k-l]%MOD)%=MOD;
				}
			}
			size[x]+=size[e[i].to];
			for (int i=1;i<=size[x];i++) f[x][i]=g[i],g[i]=0;
		}
}
int main() {
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;i++) fa[i]=i;
	for (int i=0;i<=n;i++) C[i][0]=1;
	for (int i=1;i<=n;i++)
		for (int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%MOD;
	for (int u,v,i=1;i<=m;i++) {
		char ch[2];scanf("%d%s%d",&u,ch,&v);
		if (ch[0]=='=') if (find(u)!=find(v)) fa[find(u)]=find(v);
		if (ch[0]=='<') a[++c]=(data){u,v};
	}
	for (int i=1;i<=c;i++) {
		int u=find(a[i].u),v=find(a[i].v);
		link(u,v);r[v]++;
	}
	for (int i=1;i<=n;i++) if (fa[i]==i) {
			sum++;
			if (!r[i]) link(0,i),r[i]++;
		}
	if (!bfs()) {puts("0");return 0;}
	dfs(0);
	int ans=0;
	for (int i=1;i<=n+1;i++) (ans+=f[0][i])%=MOD;
	printf("%d",ans);
	return 0;
}

 

posted @ 2017-02-23 09:19  MashiroSky  阅读(240)  评论(0编辑  收藏  举报