题解

把所有=的点连起来,一个图合法肯定它是一个有向树森林

我们新建一个点,把这个点和其他所有树的树根连起来

定义\(dp[u][j]\)表示第u个点长度为j的序列的方案数

转移方法是

\(dp[u][j] += g[k] \cdot dp[v][h] \cdot \binom{j}{k} \cdot \binom{k}{h - j + k}\)

\(g[k]\)是我们记录之前儿子合并的序列为k的方案数

就是我们确定目标序列的长度为\(j\)后,选\(k\)个位置放第一个序列,在这\(k\)个位置中再选\(h - j + k\)个作为第二个序列和第一个序列重合的部分,和之前的空位一起放第二个序列

代码

#include <bits/stdc++.h>
#define fi first
#define se second
#define pii pair<int,int>
#define mp make_pair
#define pb push_back
#define enter putchar('\n')
#define space putchar(' ')
#define MAXN 105
//#define ivorysi
using namespace std;
typedef long long int64;
typedef long double db;
typedef unsigned int u32;
template<class T>
void read(T &res) {
res = 0;char c = getchar();T f = 1;
while(c < '0' || c > '9') {
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9') {
res = res * 10 + c - '0';
c = getchar();
}
res *= f;
}
template<class T>
void out(T x) {
if(x < 0) {putchar('-');x = -x;}
if(x >= 10) out(x / 10);
putchar('0' + x % 10);
}
const int MOD = 1000000007;
int N,M,id[MAXN],cnt;
int u[MAXN],v[MAXN],f[MAXN],ind[MAXN];
char ch[MAXN][5];
int vis[MAXN],C[MAXN][MAXN],dp[MAXN][MAXN],siz[MAXN],g[MAXN],s[MAXN];
struct node {
int next,to;
}E[MAXN * 2];
int sumE,head[MAXN];
int inc(int a,int b) {
return a + b >= MOD ? a + b - MOD : a + b;
}
int mul(int a,int b) {
return 1LL * a * b % MOD;
}
void add(int u,int v) {
E[++sumE].next = head[u];
E[sumE].to = v;
head[u] = sumE;
}
int getf(int u) {
return f[u] == u ? u : f[u] = getf(f[u]);
}
bool check(int u) {
vis[u] = 1;
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
if(!vis[v]) {if(!check(v)) return false;}
if(vis[v] == 1) return false;
}
vis[u] = 2;
return true;
}
void Init() {
read(N);read(M);
for(int i = 1 ; i <= N ; ++i) f[i] = i;
for(int i = 1 ; i <= M ; ++i) {
read(u[i]);scanf("%s",ch[i]);read(v[i]);
if(ch[i][0] == '=') {
f[getf(u[i])] = getf(v[i]);
}
}
for(int i = 1 ; i <= N ; ++i) {
if(!id[getf(i)]) id[getf(i)] = ++cnt;
}
for(int i = 1 ; i <= M ; ++i) {
if(ch[i][0] == '<') {
add(id[getf(u[i])],id[getf(v[i])]);
ind[id[getf(v[i])]]++;
}
}
N = cnt + 1;
for(int i = 1 ; i <= cnt ; ++i) if(!ind[i]) add(N,i);
C[0][0] = 1;
for(int i = 1 ; i <= 101 ; ++i) {
C[i][0] = 1;
for(int j = 1 ; j <= i ; ++j) {
C[i][j] = inc(C[i - 1][j],C[i - 1][j - 1]);
}
}
}
void dfs(int u) {
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
dfs(v);
}
memset(g,0,sizeof(g));
g[0] = 1;
for(int i = head[u] ; i ; i = E[i].next) {
int v = E[i].to;
for(int j = 0 ; j <= siz[u] + siz[v] ; ++j) {
s[j] = 0;
for(int k = 0 ; k <= siz[v] ; ++k) {
if(k > j) break;
for(int h = 0 ; h <= siz[u] ; ++h) {
if(h > j) break;
s[j] = inc(s[j],mul(mul(dp[v][k],g[h]),mul(C[j][k],C[k][h - j + k])));
}
}
}
for(int j = 0 ; j <= siz[u] + siz[v] ; ++j) g[j] = s[j];
siz[u] += siz[v];
}
++siz[u];
for(int j = 1 ; j <= siz[u] ; ++j) dp[u][j] = g[j - 1];
}
void Solve() {
for(int i = 1 ; i <= N ; ++i) {
if(!vis[i] && !check(i)) {puts("0");return;}
}
dfs(N);
int ans = 0;
for(int i = 0 ; i <= N ; ++i) ans = inc(ans,dp[N][i]);
out(ans);enter;
}
int main() {
#ifdef ivorysi
freopen("f1.in","r",stdin);
#endif
Init();
Solve();
return 0;
}

【LOJ】#2117. 「HNOI2015」实验比较的更多相关文章

  1. loj #2116. 「HNOI2015」开店

    #2116. 「HNOI2015」开店 题目描述 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的想法当然非 ...

  2. Loj #2192. 「SHOI2014」概率充电器

    Loj #2192. 「SHOI2014」概率充电器 题目描述 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品--概率充电器: 「采用全新纳米级加工技术,实现元件与导线能否通电完 ...

  3. Loj #3096. 「SNOI2019」数论

    Loj #3096. 「SNOI2019」数论 题目描述 给出正整数 \(P, Q, T\),大小为 \(n\) 的整数集 \(A\) 和大小为 \(m\) 的整数集 \(B\),请你求出: \[ \ ...

  4. Loj #3093. 「BJOI2019」光线

    Loj #3093. 「BJOI2019」光线 题目描述 当一束光打到一层玻璃上时,有一定比例的光会穿过这层玻璃,一定比例的光会被反射回去,剩下的光被玻璃吸收. 设对于任意 \(x\),有 \(x\t ...

  5. Loj #3089. 「BJOI2019」奥术神杖

    Loj #3089. 「BJOI2019」奥术神杖 题目描述 Bezorath 大陆抵抗地灾军团入侵的战争进入了僵持的阶段,世世代代生活在 Bezorath 这片大陆的精灵们开始寻找远古时代诸神遗留的 ...

  6. Loj #2542. 「PKUWC2018」随机游走

    Loj #2542. 「PKUWC2018」随机游走 题目描述 给定一棵 \(n\) 个结点的树,你从点 \(x\) 出发,每次等概率随机选择一条与所在点相邻的边走过去. 有 \(Q\) 次询问,每次 ...

  7. Loj #3059. 「HNOI2019」序列

    Loj #3059. 「HNOI2019」序列 给定一个长度为 \(n\) 的序列 \(A_1, \ldots , A_n\),以及 \(m\) 个操作,每个操作将一个 \(A_i\) 修改为 \(k ...

  8. Loj #3056. 「HNOI2019」多边形

    Loj #3056. 「HNOI2019」多边形 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时针方向标号依次为 \(1,2,3, \ldots , n\).最开 ...

  9. LOJ #2135. 「ZJOI2015」幻想乡战略游戏(点分树)

    题意 给你一颗 \(n\) 个点的树,每个点的度数不超过 \(20\) ,有 \(q\) 次修改点权的操作. 需要动态维护带权重心,也就是找到一个点 \(v\) 使得 \(\displaystyle ...

随机推荐

  1. 【刷题】LOJ 6224 「网络流 24 题」深海机器人问题

    题目描述 深海资源考察探险队的潜艇将到达深海的海底进行科学考察. 潜艇内有多个深海机器人.潜艇到达深海海底后,深海机器人将离开潜艇向预定目标移动. 深海机器人在移动中还必须沿途采集海底生物标本.沿途生 ...

  2. 【刷题】LOJ 6008 「网络流 24 题」餐巾计划

    题目描述 一个餐厅在相继的 \(n\) 天里,每天需用的餐巾数不尽相同.假设第 \(i\) 天需要 \(r_i\) 块餐巾.餐厅可以购买新的餐巾,每块餐巾的费用为 \(P\) 分:或者把旧餐巾送到快洗 ...

  3. ARG102E:Stop. Otherwise...

    传送门 Sol 对于每个 \(i\) ,可以把 \(k\) 个数字分成 \((x,i-x)\) 的若干组. 那么就是求每组只能其中选择一个且可以重复的方案数. 预处理 \(f[i][j]\) 表示从 ...

  4. 【转】AD转换中常用的十种数字滤波法

    在AD采集中经常要用到数字滤波,而不同情况下又有不同的滤波需求,下面是10种经典的软件滤波方法的程序和优缺点分析: 限幅滤波法(又称程序判断滤波法) 中位值滤波法 算术平均滤波法 递推平均滤波法(又称 ...

  5. eclipse java build path问题汇总

    背景:在项目开发过程中,很多应用都进行了模块划分,有的时候是jar包依赖,有的时候通过build path进行配置,搞清楚这部分有助于理解项目之间的关系. 1 tms项目开发 1.1 问题描述 项目结 ...

  6. Linux命令(七)Linux用户管理和修改文件权限

    1. 用户管理 1.1 创建用户/设置密码/删除用户 (-m很重要,自动添加用户家目录) 创建用户组dev, 给用户组dev新建xiaoqin用户,给新用户设置密码! 1.2 查看用户信息 1.3 设 ...

  7. 安装mongodb以及设置为windows服务 详细步骤

    我的win7 32的,注意版本要正确! 一.下载mongodb压缩包:mongodb-win32-i386-2.6.9.zip() 二.在D盘新建文件夹mongodb,将压缩我的解压文件放进去(有一个 ...

  8. 如何使用ASP.NET开发基于推技术的聊天室?

    public class Content : System.Web.UI.Page{private void Page_Load(object sender, System.EventArgs e){ ...

  9. 20155215 2016-2017-2 《Java程序设计》第7周学习总结

    20155215 2016-2017-2 <Java程序设计>第7周学习总结 教材学习内容总结 第十二章 lambda语法:Lambda去重复,回忆DRY原则,Lambda表达式可读性更好 ...

  10. linux - JDK 环境

    JDK安装 vi /etc/profile # 添加环境变量 export JAVA_HOME=/usr/local/jdk1.8.2_45 export CLASSPATH=.:$JAVA_HOME ...