题面

题解

首先将所有相等的用并查集缩点,然后会发现题目有一个很有用的性质:

对每张图片\(i\),小D都最多只记住了某一张质量不比\(i\)差的另一张图片\(K_i\)。

于是将\(K_i\)作为\(i\)的父亲节点,对于\(K_i = 0\)的点,令\(i\)的父亲为\(n + 1\)即可。

开始树形\(dp\),设\(f[x][i]\)表示\(dp\)到点\(x\),有\(i - 1\)个小于号的方案数。

那么我们可以推出一个式子:

\[f[x][i] = \sum_{to \in son(i), j, k} f'[x][j] \times f[to][k] \times \xi
\]

其中\(\xi\)是表示\(j\)段和\(k\)段合并成\(i\)段的方案数。

我们考虑如何求余项\(\xi\),设\(f[x]\)的质量序列为\(A\),\(f'[x]\)的质量序列为\(B\),\(f[to]\)的质量序列为\(C\)。

\(A\)的每一段可以只包含\(B\)的一段或者\(C\)的一段,也可以同时包含,但不能为空。特殊地,\(A\)的第一段只能包含节点\(x\)。

于是\(\xi\)相当于先枚举\(B\)中的\(j - 1\)段在\(A\)中放的位置,方案数为\(\binom{i - 1}{j - 1}\),然后将\(C\)的\(i - j\)段放在\(A\)中剩下的位置,使得每一段都不为空。现在\(C\)中还有\(k - i + j\)段要与\(B\)中的段合并,方案数为\(\binom{j - 1}{k - i + j}\)。

于是:

\[\xi = \binom{i - 1}{j - 1} \times \binom{j - 1}{k - i + j}
\]

最后答案为\(\sum_{i = 1}^{\mathrm{size}(n + 1)} f[n + 1][i]\)

复杂度:因为每个点对只会在LCA处算\(\mathrm{O}(n)\)次,于是复杂度是\(\mathrm{O}(n^3)\)

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define RG register inline int read()
{
int data = 0, w = 1; char ch = getchar();
while(ch != '-' && (!isdigit(ch))) ch = getchar();
if(ch == '-') w = -1, ch = getchar();
while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
return data * w;
} inline char get()
{
char ch = getchar();
while(ch != '<' && ch != '=') ch = getchar();
return ch;
} const int N(105), Mod(1e9 + 7), M(100);
struct edge { int next, to; } e[N << 1];
int n, m, X[N], Y[N], fa[N], eq[N], size[N], e_num;
int belong[N], cnt[N], head[N], C[N][N], f[N][N];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
inline void add_edge(int from, int to)
{
e[++e_num] = (edge) {head[from], to}; head[from] = e_num;
e[++e_num] = (edge) {head[to], from}; head[to] = e_num;
} inline int merge(int x, int y)
{
int fx = find(x), fy = find(y);
fa[fx] = fy; return fx == fy;
} void dfs(int x, int _f)
{
static int g[N]; size[x] = f[x][1] = 1;
for(RG int p = head[x]; p; p = e[p].next)
{
int to = e[p].to; if(to == _f) continue;
dfs(to, x); std::fill(g + 1, g + n + 1, 0);
for(RG int i = 1; i <= size[x] + size[to]; i++)
for(RG int j = 1; j <= size[x]; j++)
for(RG int k = 1; k <= size[to]; k++)
{
int t = k - i + j; if(t < 0) continue;
g[i] = (g[i] + 1ll * f[x][j] * f[to][k] % Mod *
C[i - 1][j - 1] % Mod * C[j - 1][t]) % Mod;
}
size[x] += size[to];
std::copy(g + 1, g + size[x] + 1, f[x] + 1);
}
} int main()
{
C[0][0] = 1;
for(RG int i = 1; i <= M; i++)
{
C[i][0] = C[i][i] = 1;
for(RG int j = 1; j < i; j++)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % Mod;
}
n = read(), m = read();
for(RG int i = 1; i <= n; i++) fa[i] = i;
for(RG int i = 1; i <= m; i++)
X[i] = read(), eq[i] = get() == '=', Y[i] = read();
for(RG int i = 1; i <= m; i++) if(eq[i]) fa[find(X[i])] = find(Y[i]);
for(RG int i = 1; i <= n; i++) belong[i] = find(i);
for(RG int i = 1; i <= n; i++) fa[i] = i;
for(RG int i = 1; i <= m; i++) if(!eq[i])
{
add_edge(belong[X[i]], belong[Y[i]]), ++cnt[belong[Y[i]]];
if(merge(belong[X[i]], belong[Y[i]])) return puts("0") & 0;
}
for(RG int i = 1; i <= n; i++)
if(belong[i] == i && !cnt[i]) add_edge(n + 1, i);
int ans = 0; dfs(n + 1, 0);
for(RG int i = 1; i <= size[n + 1]; i++)
ans = (ans + f[n + 1][i]) % Mod;
printf("%d\n", ans);
return 0;
}

【HNOI2015】实验比较的更多相关文章

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

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

  2. 4013: [HNOI2015]实验比较

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

  3. [BZOJ4013][HNOI2015]实验比较(树形DP)

    4013: [HNOI2015]实验比较 Time Limit: 5 Sec  Memory Limit: 512 MBSubmit: 756  Solved: 394[Submit][Status] ...

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

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

  5. [HNOI2015]实验比较

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

  6. P3240 [HNOI2015]实验比较 树形DP

    \(\color{#0066ff}{ 题目描述 }\) 小D 被邀请到实验室,做一个跟图片质量评价相关的主观实验.实验用到的图片集一共有 \(N\) 张图片,编号为 \(1\) 到\(N\).实验分若 ...

  7. BZOJ4013 : [HNOI2015]实验比较

    首先用并查集将等号缩点,然后拓扑排序判断有没有环,有环则无解,否则通过增加超级源点$0$,可以得到一棵树. 设$f[x][y]$表示$x$子树里有$y$种不同的数字的方案数,由底向上DP. 对于当前点 ...

  8. luogu P3240 [HNOI2015]实验比较

    传送门 首先根据题目条件,题目中如果是=的点可以缩起来,然后\(a<b\)连边\(a\rightarrow b\),而且所有点入度为最多1,那么判掉有环的不合法情况,题目中的依赖关系就是一颗外向 ...

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

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

  10. 【LG3240】[HNOI2015]实验比较

    题面 洛谷 题解 30pts 爆搜即可. 100pts 题意描述里有一句:"对每张图片\(i\),小\(D\)都最多只记住了某一张质量不比\(i\)差的另一张图片\(K_i\)." ...

随机推荐

  1. java vector的多线程安全是否有用

    在网上搜了不少文章,发现有不少没讲清楚的,也有不少好文,本文希望更易懂地描述该问题.如有不对的地方,请多多指正~~ vector的使用主要有如下两种场景:(1)vector所谓的多线程安全,只是针对单 ...

  2. Linux less/more命令详解

    less 的用法比起 more 更加的有弹性.在 more 的时候,我们并没有办法向前面翻, 只能往后面看,但若使用了 less 时,就可以使用 [pageup] [pagedown] 等按键的功能来 ...

  3. Go语言-windows安装配置篇

    Go-windows安装配置 前言 学习完了python基础,顺便也要提前学习一下go啦,抱着这样的心情,今晚尝试了安装一下go,很顺利的完成了,没有难度. 需要了解更多的关于Go的基本信息可以去维基 ...

  4. 铁乐学python_day28_模块学习3

    大部份内容摘自授课老师的博客http://www.cnblogs.com/Eva-J/ OS模块复习一二 >>> import os >>> os.getcwd() ...

  5. unix时间戳的转换

    UNIX时间戳转换为日期用函数: FROM_UNIXTIME() select FROM_UNIXTIME(1156219870); 日期转换为UNIX时间戳用函数: UNIX_TIMESTAMP() ...

  6. UI(三)

    1. 2.经常用到的loadmap函数 void CTopology::LoadMap() { //m_map.RemoveAllLayers(); AddLayersBasemap(); AddLa ...

  7. PHP设计模式系列 - 中介者模式

    中介者模式 中介者模式用于开发一个对象,这个对象能够在类似对象相互之间不直接相互的情况下传送或者调解对这些对象的集合的修改.一般处理具有类似属性,需要保持同步的非耦合对象时,最佳的做法就是中介者模式. ...

  8. 【原创】uwsgi中多进程+多线程原因以及串行化accept() - thunder_lock说明

    如有不对,请详细指正. 最近再研究uwsgi如何部署python app,看uwsgi的文档,里面有太多的参数,但每个参数的解释太苍白,作为菜鸟的我实在是不懂.想搞清楚uwsgi的工作原因以及里面的一 ...

  9. Python的多线程理解,转自虫师https://www.cnblogs.com/fnng/p/3670789.html

    多线程和多进程是什么自行google补脑 对于python 多线程的理解,我花了很长时间,搜索的大部份文章都不够通俗易懂.所以,这里力图用简单的例子,让你对多线程有个初步的认识. 单线程 在好些年前的 ...

  10. 在.NET中操作数字证书(新手教程)

    .NET为我们提供了操作数字证书的两个主要的类,分为为: System.Security.Cryptography.X509Certificates.X509Certificate2类, 每个这个类的 ...