题目传送门

分析

放一个dalao博客: xyz32768 的博客,看完再回来看本蒟蒻的口胡吧(其实嘛…不回来也行)

  • 精髓是合并的方案数的计算,至于为什么是Ci−1j−1\large C_{i-1}^{j-1}Ci−1j−1​,是因为当前点必须独立成为第一部分
  • 时间复杂度的O(n3)O(n^3)O(n3)也是个玄学东西。其实是因为枚举j,kj,kj,k时上限分别是sz[u]sz[u]sz[u](到目前所有子树的大小)和sz[v]sz[v]sz[v](这棵子树的大小),乘起来就是相当于在uuu下方中枚举不同子树内的点对。那么每一对只会在lcalcalca处被枚举到。因为在lcalcalca下方,两个点不可能一起被枚举到;而在lcalcalca上方,它们已经存在于同一子树了。所以这个枚举总时间复杂度是O(n2)O(n^2)O(n2)。最终复杂度就为O(n3)O(n^3)O(n3)
  • 注意判断环的方式有没有考虑周全。因为不能用简单的用入度为000来判断每一棵树的根节点(因为可能有环),我写的naivenaivenaive的O(n)O(n)O(n)的判环各种姿势WAWAWA,见下
  • 第一发

  • 第二发





  • 。。。于是直接O(n2)O(n^2)O(n2)每次清零visvisvis数组跑dfsdfsdfs。能过就行。
  • CODE

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 105;
const int mod = 1e9+7;
int n, m, edgecnt;
struct edge { int u, v; }e[MAXN]; int scc[MAXN];
int find(int x) { return scc[x] == x ? x : scc[x]=find(scc[x]); } int fir[MAXN], to[MAXN], nxt[MAXN], cnt, in[MAXN];
inline void add(int u, int v) { to[++cnt] = v; nxt[cnt] = fir[u]; fir[u] = cnt; in[v]++; } bool vis[MAXN];
bool check(int u) {
if(vis[u]) return 0;
vis[u] = 1;
for(int i = fir[u]; i; i = nxt[i])
if(!check(to[i])) return 0;
return 1;
} int f[MAXN][MAXN], sz[MAXN], tmp[MAXN], c[MAXN][MAXN];
void dp(int u) {
f[u][1] = sz[u] = 1;
for(int l = fir[u], v; l; l = nxt[l]) {
dp(v = to[l]);
for(int i = 1; i <= sz[u]+sz[v]; ++i) tmp[i] = 0;
for(int i = 1; i <= sz[u]+sz[v]; ++i)
for(int j = 1; j <= sz[u] && j <= i; ++j)
for(int k = 1; k <= sz[v] && k <= i; ++k)
if(k + j >= i)
tmp[i] = (tmp[i] + 1ll * f[u][j] * f[v][k] % mod * c[i-1][j-1] % mod * c[j-1][k-(i-j)] % mod) % mod;
for(int i = 1; i <= sz[u]+sz[v]; ++i) f[u][i] = tmp[i];
sz[u] += sz[v];
}
} int main () {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scc[i] = i;
char s[2];
for(int i = 1, x, y; i <= m; ++i) {
scanf("%d%s%d", &x, s, &y);
if(s[0] == '=') scc[find(y)]=find(x);
else {
if(s[i] == '>') swap(x, y); //emmm... unnecessary
e[++edgecnt] = (edge){ x, y };
}
}
for(int i = 1; i <= n; ++i) find(i);
for(int i = 1; i <= edgecnt; ++i) {
if(scc[e[i].u] == scc[e[i].v]) return puts("0"), 0;
add(scc[e[i].u], scc[e[i].v]);
}
bool flg = 1;
for(int i = 1; i <= n; ++i) if(scc[i] == i) {
memset(vis, 0, sizeof vis);
if(!check(i)) { flg = 0; break; } //judge the circle
if(!in[i]) add(0, i);
}
if(!flg) return puts("0"), 0;
c[0][0] = 1;
for(int i = 1; i <= n; ++i) {
c[i][0] = c[i][i] = 1;
for(int j = 1; j < i; ++j)
c[i][j] = (c[i-1][j-1] + c[i-1][j]) % mod;
}
dp(0);
int ans = 0;
for(int i = 1; i <= sz[0]; ++i)
ans = (ans + f[0][i]) % mod;
printf("%d\n", ans);
}
  • 出于良心的UpdUpdUpd

    正确的O(n)O(n)O(n)判环方式是用inqinqinq数组存每个点是否在当前dfsdfsdfs栈中,如果下一个点已经在栈中那么存在环,returnreturnreturn时出栈。

BZOJ 4013/Luogu P3240 [HNOI2015] 实验比较 (树形DP)的更多相关文章

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

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

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

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

  3. luogu P3240 [HNOI2015]实验比较

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

  4. [HNOI2015]实验比较 树形dp+组合数学

    在合并的时候有可以加等于,或者继续用小于, 比如siz[x]和siz[y]合并,小于的区间为max(siz[x],siz[y])<=k<=siz[x]+siz[y], 然后就是合并成多少个 ...

  5. [BZOJ 1907] 树的路径覆盖 【树形DP】

    题目链接:BZOJ - 1907 题目分析 使用树形 DP,f[x][0] 表示以 x 为根的子树不能与 x 的父亲连接的最小路径数(即 x 是一个折线的拐点). f[x][1] 表示以 x 为根的子 ...

  6. bzoj 4871: [Shoi2017]摧毁“树状图” [树形DP]

    4871: [Shoi2017]摧毁"树状图" 题意:一颗无向树,选两条边不重复的路径,删去选择的点和路径剩下一些cc,求最多cc数. update 5.1 : 刚刚发现bzoj上 ...

  7. BZOJ.4199.[NOI2015]品酒大会(后缀自动机 树形DP)

    BZOJ 洛谷 后缀数组做法. 洛谷上SAM比SA慢...BZOJ SAM却能快近一倍... 只考虑求极长相同子串,即所有后缀之间的LCP. 而后缀的LCP在后缀树的LCA处.同差异这道题,在每个点处 ...

  8. BZOJ.3611.[HEOI2014]大工程(虚树 树形DP)

    题目链接 要求的和.最大值.最小值好像都可以通过O(n)的树形DP做,总询问点数<=2n. 于是建虚树就可以了.具体DP见DP()函数,维护三个值sum[],mx[],mn[]. sum[]要开 ...

  9. BZOJ 1040 ZJOI 2008 骑士 基环树林+树形DP

    题目大意:有一些骑士.他们每个人都有一个权值.可是因为一些问题,每个骑士都特别讨厌还有一个骑士.所以不能把他们安排在一起.求这些骑士所组成的编队的最大权值和是多少. 思路:首先貌似是有向图的样子,可是 ...

随机推荐

  1. [转帖]YES!AMD千元无敌U闪亮登场、16核至尊为用户着想

    YES!AMD千元无敌U闪亮登场.16核至尊为用户着想 投递人 itwriter 发布于 2019-09-30 09:34 评论(0) 有567人阅读 原文链接 [收藏] « » https://ne ...

  2. Windows 下redis的安装和使用

    1.下载 Window 下载地址:https://github.com/MSOpenTech/redis/releases 查找版本对应的一个MSI或者zip文件下载 2.安装 MSI文件需要安装 z ...

  3. linux运维工程师常用命令

    1.ls [选项] [目录名 | 列出相关目录下的所有目录和文件 -a  列出包括.a开头的隐藏文件的所有文件-A  通-a,但不列出"."和".."-l  列 ...

  4. 剑指offer(9)——用两个栈实现队列

    题目: 用两个栈实现一个队列.队列的声明如下,请实现它的两个函数appendTail和deleteHead,分别完成在队列尾部插入结点和在队列头部删除结点的功能. 思路: 首先定义两个栈stack1. ...

  5. [NOIP2018模拟赛10.23]发呆报告

    闲扯 考场看了眼题目感觉很难,一个小时敲完了所有暴力...嗯然后就在那里发呆什么事也没做 T3考场上把数据结构想了个遍都不会完成1操作,现在看这种思路其实之前也接触过... 比较玄学的一件事情就是T1 ...

  6. buffer 与 cache 的区别

    Buffer 和 Cache buffer 和 cache 同样作为缓存,他们之间有什么区别呢? 简单来说,buffer 是即将要写入磁盘的缓存,而 cache 是从磁盘中读出来放到缓存的 参考来自: ...

  7. pytorch中使用多显卡训练以及训练时报错:expect more than 1 value per channel when training, got input size..

    pytorch在训练中使用多卡: conf.device = torch.device('cuda:0' if torch.cuda.is_available() else "cpu&quo ...

  8. 用js原生加jquery实现下拉跳转至商品详情页,上拉回到商品简介

    在做一个商城的项目时,做到商品详情页的时候需要实现这种下拉跳转到商品详情页加载许多图片,上拉回到商品简介的效果,并且需要用户在滑动时有一种费力的感觉.最初是通过iscroll插件实现的,但这个插件在使 ...

  9. KVM之virsh管理虚拟机CPU

    查看虚拟机CPU数量配置 [root@ubuntu ~]# virsh vcpucount centos_server01 maximum config 2 maximum live 2 curren ...

  10. 7.Java集合-Arrays类实现原理及源码分析

    Java集合---Arrays类源码解析  转自:http://www.cnblogs.com/ITtangtang/p/3948765.html 一.Arrays.sort()数组排序 Java A ...