[HNOI2015]实验比较

Description
\(D\)被邀请到实验室,做一个跟图片质量评价相关的主观实验。实验用到的图片集一共有\(N\)张图片,编号为\(1\)\(N\)。实验分若干轮进行,在每轮实验中,小\(D\)会被要求观看某两张随机选取的图片, 然后小\(D\)需要根据他自己主观上的判断确定这两张图片谁好谁坏,或者这两张图片质量差不多。 用符号"<",">"和"="表示图片\(x\)\(y\)\(x\)\(y\)为图片编号)之间的比较:

如果上下文中\(x\)\(y\)是图片编号,则\(x<y\)表示图片\(x\)"质量优于"\(y\)\(x>y\)表示图片\(x\)"质量差于"\(y\)\(x=y\)表示图片\(x\)\(y\)"质量相同";

也就是说,这种上下文中,"<"、">"、"="分别是质量优于、质量差于、质量相同的意思;在其他上下文中,这三个符号分别是小于、大于、等于的含义。图片质量比较的推理规则(在\(x\)\(y\)是图片编号的上下文中):

(1)\(x<y\)等价于\(y>x\)

(2)若\(x<y\)\(y=z\),则\(x<z\)

(3)若\(x<y\)\(x=z\),则\(z<y\)

(4)\(x=y\)等价于\(y=x\)

(5)若\(x=y\)\(y=z\),则\(x=z\)

实验中,小\(D\)需要对一些图片对\((x, y)\),给出\(x<y\)\(x=y\)\(x>y\)的主观判断。小\(D\)在做完实验后, 忽然对这个基于局部比较的实验的一些全局性质产生了兴趣。在主观实验数据给定的情形下,定义这\(N\)张图片的一个合法质量序列为形如"\(x_1\,R_1\,x_2\,R_2\,x_3\,R_3\,…x_{N-1}\,R_{N-1}\,x_N\)"的串,也可看作是集合\(\{x_i\,R_i\,x_{i+1}|1\leqslant i\leqslant N-1\}\),其中\(x_i\)为图片编号,\(x_1,x_2,…,x_N\)两两互不相同(即不存在重复编号),\(R_i\)为<或=,"合法"是指这个图片质量序列与任何一对主观实验给出的判断不冲突。

例如: 质量序列\(3<1=2\)与主观判断"\(3>1,3=2\)"冲突(因为质量序列中\(3<1\)\(1=2\),从而\(3<2\),这与主观判断中的\(3=2\)冲突;同时质量序列中的\(3<1\)与主观判断中的\(3>1\)冲突) ,但与主观判断"\(2=1,3<2\)"不冲突;因此给定主观判断"\(3>1,3=2\)"时,\(1<3=2\)\(1<2=3\)都是合法的质量序列,\(3<1=2\)\(1<2<3\)都是非法的质量序列。由于实验已经做完一段时间了,小\(D\)已经忘了一部分主观实验的数据。对每张图片\(i\),小\(D\)都最多只记住了某一张质量不比\(i\)差的另一张图片\(K_i\)。这些小\(D\)仍然记得的质量判断一共有\(M\)\((0\leqslant M\leqslant N)\),其中第\(i\)条涉及的图片对为\((K_{X_i},X_i)\),判断要么是\(K_{X_i}<X_i\),要么是\(K_{X_i}=X_i\),而且所有的\(X_i\)互不相同。小\(D\)打算就以这\(M\)条自己还记得的质量判断作为他的所有主观数据。

现在,基于这些主观数据,我们希望你帮小\(D\)求出这\(N\)张图片一共有多少个不同的合法质量序列。

我们规定:如果质量序列中出现"\(x=y\)",那么序列中交换\(x\)\(y\)的位置后仍是同一个序列。因此:\(1<2=3=4<5\)\(1<4=2=3<5\)是同一个序列,\(1<2=3\)\(1<3=2\)是同一个序列,而\(1<2<3\)\(1<2=3\)是不同的序列,\(1<2<3\)\(2<1<3\)是不同的序列。由于合法的图片质量序列可能很多, 所以你需要输出答案对\(10^9+7\)取模的结果

Input
第一行两个正整数\(N,M\),分别代表图片总数和小\(D\)仍然记得的判断的条数;
接下来\(M\)行,每行一条判断,每条判断形如"\(x<y\)"或者"\(x=y\)"​。

Output
输出仅一行,包含一个正整数,表示合法质量序列的数目对 10^9+7取模的结果。

Sample Input
5 4
1 < 2
1 < 3
2 < 4
1 = 5

Sample Output
5

HINT
不同的合法序列共5个,如下所示:
1 = 5 < 2 < 3 < 4
1 = 5 < 2 < 4 < 3
1 = 5 < 2 < 3 = 4
1 = 5 < 3 < 2 < 4
1 = 5 < 2 = 3 < 4
100%的数据满足N<=100。


首先注意到题目中很重要的一句话,对每张图片\(i\),小\(D\)都最多只记住了某一张质量不比\(i\)差的另一张图片\(K_i\),于是我们将所有等于关系的点缩到一起,对于点\(i\),如果\(K_i\)存在,则\(i\)\(K_i\)连边,那么显然构成了森林,我们用超级root把森林变成树

然后tree dp,设\(f[u]\)表示\(u\)的子树内的方案数

但是对于\(u\)的两个子树\(v,w\),可能存在一些点质量相同,因此我们需要再添加一维,设\(f[u][i]\)表示\(u\)的子树内分成\(i\)端的方案数(每段里面的节点质量相等),设\(f'\)表示更新之前的\(f\)值,转移有

\[f[u][i]=\sum\limits_{j,k}f'[u][j]\times f[v][k]\times T \]

\(T\)表示\(j\)段和\(k\)段合并成\(i\)段的方案数

\(f[u]\)的质量序列为\(A\)\(f'[u]\)的质量序列为\(B\)\(f[v]\)的质量序列为\(C\),把他们合并起来相当于枚举\(B\)中的\(j-1\)段在\(A\)中的位置(起始端点已经被\(u\)霸占),那么方案为\(\binom{i-1}{j-1}\),然后把\(C\)中的\(i-j\)段放置到\(A\)中使得\(A\)非空,剩下的与\(B\)合并,方案为\(\binom{j-1}{k-i+j}\),则\(T=\binom{i-1}{j-1}\times\binom{j-1}{k-i+j}\)

答案即为\(Ans=\sum f[root][i]\),由于每对点都只在lca处被计算贡献了\(O(n)\)次,因此复杂度为\(O(n^3)\)

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline char gc(){
	static char buf[1000000],*p1=buf,*p2=buf;
	return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
}
inline int frd(){
	int x=0,f=1; char ch=gc();
	for (;ch<'0'||ch>'9';ch=gc())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=gc())	x=(x<<3)+(x<<1)+ch-'0';
	return x*f;
}
inline int read(){
	int x=0,f=1; char ch=getchar();
	for (;ch<'0'||ch>'9';ch=getchar())	if (ch=='-')	f=-1;
	for (;ch>='0'&&ch<='9';ch=getchar())	x=(x<<3)+(x<<1)+ch-'0';
	return x*f;
}
inline void print(int x){
	if (x<0)	putchar('-'),x=-x;
	if (x>9)	print(x/10);
	putchar(x%10+'0');
}
const int N=1e2,Mod=1e9+7;
int pre[(N<<1)+10],now[N+10],child[(N<<1)+10],tot;
int fa[N+10],X[N+10],Y[N+10],top[N+10],dge[N+10],size[N+10];
int f[N+10][N+10],C[N+10][N+10],Ans;
bool L[N+10],Endl[N+10];
void prepare(){
	for (int i=0;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;
	}
}
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
bool merge(int x,int y){
	x=find(x),y=find(y);
	return x!=y?fa[x]=y,0:1;
}
void join(int x,int y){pre[++tot]=now[x],now[x]=tot,child[tot]=y;}
void insert(int x,int y){join(x,y),join(y,x);}
void dfs(int x,int fa){
	f[x][1]=size[x]=1;
	for (int p=now[x],son=child[p];p;p=pre[p],son=child[p]){
		if (son==fa)	continue;
		dfs(son,x);
		static int g[N+10];
		for (int i=1;i<=size[x]+size[son];i++){
			g[i]=0;
			for (int j=1;j<=size[x];j++){
				for (int k=1;k<=size[son];k++){
					if (k-i+j<0)	continue;
					g[i]=(g[i]+1ll*f[x][j]*f[son][k]%Mod*C[i-1][j-1]%Mod*C[j-1][k-i+j]%Mod)%Mod;
				}
			}
		}
		for (int i=1;i<=size[x]+size[son];i++)	f[x][i]=g[i];
		size[x]+=size[son];
	}
}
int main(){
	prepare();
	int n=read(),m=read();
	for (int i=1;i<=n;i++)	fa[i]=i;
	for (int i=1;i<=m;i++){
		char ch[5];
		scanf("%d%s%d",X+i,ch,Y+i);
		L[i]=(ch[0]=='=');
		if (L[i])	merge(X[i],Y[i]);
	}
	for (int i=1;i<=n;i++)	Endl[top[i]=find(i)]=1;
	for (int i=1;i<=n;i++)	fa[i]=i;
	for (int i=1;i<=m;i++){
		if (L[i])	continue;
		insert(top[X[i]],top[Y[i]]);
		dge[top[Y[i]]]++;
		if (merge(top[X[i]],top[Y[i]]))	return printf("0\n"),0;
	}n++;
	for (int i=1;i<n;i++)	if (Endl[i]&&!dge[i])	insert(n,i);
	dfs(n,0);
	for (int i=1;i<=size[n];i++)	Ans=(Ans+f[n][i])%Mod;
	printf("%d\n",Ans);
	return 0;
}
posted @ 2019-01-11 10:43  Wolfycz  阅读(308)  评论(0编辑  收藏  举报