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} \cdots x_{N-1}\;R_{N-1}\;x_{N}\)”的串,也可看作是集合\(\lbrace x_{i}\;R_{i}\;x_{i+1} \mid 1 \le i \le N-1 \rbrace\),其中\(x_{i}\)为图片编号,\(x_{1},x_{2},\cdots,x_{N}\)两两互不相同(即不存在重复编号),\(R_{i}\)为<或=,“合法”是指这个图片质量序列与任何一对主观实验给出的判断不冲突。 由于实验已经做完一段时间了,小D已经忘了一部分主观实验的数据。对每张图片\(i\),小 D 都{最多只记住了某一张质量不比\(i\)差的另一张图片\(K_{i}\)。这些小\(D\)仍然记得的质量判断一共有\(M\)条\((0 \le M \le 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
\(N \le 100\)
将\(=\)缩掉之后如果该图没有环,那么一定是一棵森林结构,我们增添超级根,就变成了一颗树的结构了。
如果\(A,B\)可以用\(=\)关系的话,那么\(A,B\)一定没有子树关系。所以,我们可以想到状态\(f_{i,j}\)表示以\(i\)为根的子树,有\(j\)段(\(j-1\)个\(<\)的方案数)。利用辅助数组进行转移\(g_{i,j,k}\)表示\(i\)的前\(j\)个儿子,分成了\(k\)段的方案数。那么我们就有转移$$g_{i,j,z} = \sum C_{z}^{x} C_{x}^{x+y-z} g_{i,j-1,x} \times f_{son,y}(max(x,y) \le z \le x+y)$$
其中\(C_{z}^{x} C_{x}^{x+y-z}\)是转移系数,表示有\(x\)个白球,\(y\)个黑球,放进\(z\)个槽中,每个位置不能有两个颜色一样的球的方案数。
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<queue>
using namespace std;
typedef long long ll;
#define rhl (1000000007)
#define maxn (110)
int father[maxn],side[maxn],next[maxn],toit[maxn],g[2][maxn][maxn];
int tot,num,cnt,ind[maxn],C[maxn][maxn],f[maxn][maxn],ans,N,M;
bool exist[maxn];
struct node { int a,b; }edge[maxn];
inline int find(int a) { if (father[a] != a) father[a] = find(father[a]); return father[a]; }
inline void add(int a,int b)
{
next[++cnt] = side[a]; side[a] = cnt;
toit[cnt] = b; ++ind[b]; exist[a] = exist[b] = true;
}
inline bool topsort()
{
queue <int> team;
for (int i = 1;i <= N;++i) if (exist[i]&&!ind[i]) team.push(i),add(0,i);
int ret = 0;
while (!team.empty())
{
++ret; int now = team.front(); team.pop();
for (int i = side[now];i;i = next[i])
if (!--ind[toit[i]]) team.push(toit[i]);
}
return ret == num;
}
inline int dp(int now)
{
int sz = 0,tmp,cur = 1;
g[0][now][0] = 1;
for (int i = side[now],v;i;i = next[i],cur ^= 1)
{
tmp = dp(v = toit[i]);
for (int x = 0;x <= sz;++x)
for (int y = 1;y <= tmp;++y)
for (int z = max(x,y);z <= x+y;++z)
{
g[cur][now][z] += (ll)g[cur^1][now][x]*f[v][y]%rhl*(ll)C[z][x]%rhl*(ll)C[x][x+y-z]%rhl;
if (g[cur][now][z] >= rhl) g[cur][now][z] -= rhl;
}
memset(g[cur^1][now],0,sizeof(g[cur^1][now]));
sz += tmp;
}
for (int i = 0;i <= sz;++i) f[now][i+1] = g[cur^1][now][i];
return sz+1;
}
int main()
{
freopen("4013.in","r",stdin);
freopen("4013.out","w",stdout);
scanf("%d %d",&N,&M); num = N;
for (int i = 1;i <= N;++i) father[i] = i;
for (int i = 1,a,b;i <= M;++i)
{
char c;
scanf("%d %c %d\n",&a,&c,&b);
if (c == '<') edge[++tot] = (node){a,b};
else
{
int r1 = find(a),r2 = find(b);
if (r1 != r2) father[r1] = r2,--num;
}
}
for (int i = 1;i <= tot;++i) add(find(edge[i].a),find(edge[i].b));
if (!topsort()) printf("0");
else
{
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];
if (C[i][j] >= rhl) C[i][j] -= rhl;
}
}
int sz = dp(0);
for (int i = 1;i <= sz;++i) { ans += f[0][i]; if (ans >= rhl) ans -= rhl; }
printf("%d",ans);
}
fclose(stdin); fclose(stdout);
return 0;
}