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

BZOJ 4013 实验比较的更多相关文章

  1. bzoj 4013: [HNOI2015]实验比较

    Description 小D 被邀请到实验室,做一个跟图片质量评价相关的主观实验.实验用到的图片集一共有 N 张图片,编号为 1 到 N.实验分若干轮进行,在每轮实验中,小 D会被要求观看某两张随机选 ...

  2. BZOJ 4013 【HNOI2015】 实验比较

    题目链接:实验比较 如果我们把相等关系全部缩起来的话,这道题给出的小于关系如果有环,那么就是不合法的,否则就构成了一片森林. 定义等于号连起来的所有变量看做一个块. 然后我们就可以令\(f_{i,j} ...

  3. BZOJ 4013/Luogu P3240 [HNOI2015] 实验比较 (树形DP)

    题目传送门 分析 放一个dalao博客: xyz32768 的博客,看完再回来看本蒟蒻的口胡吧(其实嘛-不回来也行) 精髓是合并的方案数的计算,至于为什么是Ci−1j−1\large C_{i-1}^ ...

  4. 【BZOJ】4013: [HNOI2015]实验比较

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=4013 中第i 条涉及的图片对为(KXi, Xi),判断要么是KXi < Xi ,要么 ...

  5. 4013: [HNOI2015]实验比较

    4013: [HNOI2015]实验比较 链接 分析: 首先把等号用并查集合并起来. 由于只存在最多一个质量不比i差的数,发现这是森林.若x<y,连边x->y.于是建虚拟根节点0. 然后树 ...

  6. 【BZOJ】【3280】小R的烦恼

    网络流/费用流 和软件开发那题基本相同,只是多加了一个“雇佣研究生”的限制:不同价格的研究生有不同的数量…… 那么只需加一个附加源点,对每一种研究生连边 S->ss 容量为l[i],费用为p[i ...

  7. BZOJ 3786 星系探索

    Description 物理学家小C的研究正遇到某个瓶颈. 他正在研究的是一个星系,这个星系中有n个星球,其中有一个主星球(方便起见我们默认其为1号星球),其余的所有星球均有且仅有一个依赖星球.主星球 ...

  8. bzoj 4824: [Cqoi2017]老C的键盘

    Description 老 C 是个程序员.     作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序 在某种神奇力量的驱使之下跑得非常快.小 ...

  9. 【BZOJ4013】[HNOI2015]实验比较(动态规划)

    [BZOJ4013][HNOI2015]实验比较(动态规划) 题面 BZOJ 洛谷 题解 看题目意思就是给你一棵树,连边表示强制顺序关系.然后你要给点染色,在满足顺序关系的情况下,将序列染成若干个颜色 ...

随机推荐

  1. hadoop错误java.io.IOException Failed to replace a bad datanode on the existing pipeline due to no more good datanodes being available to try

    错误: java.io.IOException: Failed to replace a bad datanode on the existing pipeline due to no more go ...

  2. MapReduce中的Join算法

    在关系型数据库中Join是非常常见的操作,各种优化手段已经到了极致.在海量数据的环境下,不可避免的也会碰到这种类型的需求,例如在数据分析时需要从不同的数据源中获取数据.不同于传统的单机模式,在分布式存 ...

  3. GUI之CCControlExtension

    Introduction CCControl is inspired by the UIControl API class from the UIKit library of CocoaTouch. ...

  4. oracle修改登录认证方式

    通过配置sqlnet.ora文件,我们可以修改oracle登录认证方式. SQLNET.AUTHENTICATION_SERVICES=(NTS);基于操作系统的认证 SQLNET.AUTHENTIC ...

  5. javascript split() 正则表达式

    路由匹配 http.createServer(function(req, res) { var items = req.url.split('/'); if (items.length < 3 ...

  6. WPF – 使用触发器

    WPF – 使用触发器 WPF提供了很重要的一个东西就是绑定Binding, 它帮助我们做了很多事情,这个我们在WPF学习之绑定这篇里边有讲过.对于Binding我们可以设置其绑定对象,关系,并通过某 ...

  7. C#基础入门--关于C#背景介绍以及变量相关

    在正式探索C#的奥秘之前,我们先谈一谈关于学习方法的问题吧.你会不会有这样的感悟,自己努力奋斗得到的东西倍加珍惜,飘到眼前的,却不屑一顾.我认为,学习的整个历程亦是如此.在学习过程中,只有我们遇到了问 ...

  8. 【锋利的jQuery】学习笔记02

    第二章 jQuery选择器 一.jQuery选择器的优势 写法简洁 $("div") 支持css2和css3选择器(对于css3选择器支持这一项,我认为应该是jQuery首先创造并 ...

  9. JS手动创建标签

    代码: <html> <head> <title>js标签属性的添加</title> <script > function setxxx() ...

  10. JAVA 循环在一个数字前面填充0.小例子

    输入结果 00000000000567 String bala="567"; 固定长度是14位,怎么循环在bala前面填充00000000000 System.out.printl ...