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}|1leqslant ileqslant 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)条((0leqslant Mleqslant 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)值,转移有
(T)表示(j)段和(k)段合并成(i)段的方案数
设(f[u])的质量序列为(A),(f'[u])的质量序列为(B),(f[v])的质量序列为(C),把他们合并起来相当于枚举(B)中的(j-1)段在(A)中的位置(起始端点已经被(u)霸占),那么方案为(inom{i-1}{j-1}),然后把(C)中的(i-j)段放置到(A)中使得(A)非空,剩下的与(B)合并,方案为(inom{j-1}{k-i+j}),则(T=inom{i-1}{j-1} imesinom{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
"),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
",Ans);
return 0;
}