首页 > 代码库 > 【bzoj4013】 HNOI2015—实验比较

【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;}

 

【bzoj4013】 HNOI2015—实验比较