[BZOJ4013][HNOI2015]实验比较(树形DP+组合数学)
2018-02-20 20:53
531 查看
先用并查集将所有用等号连接的点缩成一个。然后看到题目中有一个很重要的条件:
对每张图片ii,小D都最多只记住了某一张质量不比ii差的另一张图片KiKi。
缩点后建树,对于每个ii,如果KiKi存在,就将KiKi作为ii的父节点。建树后有可能是一棵森林,所以新建一个新的节点n+1n+1连接森林里每棵树的根节点,形成一棵树,n+1n+1为根。
然后树形DP,f[u]f[u]表示uu的子树内的方案数。
但对于uu的两个不同子节点vv和ww,vv和ww的子树内可能存在两个点质量相等,所以还需要加一维:
f[u][i]f[u][i]表示uu的子树里,分成ii段(也就是共有i−1i−1个小于号把质量序列分成了ii个部分,每个部分里的图片质量相等)的方案数,然后做一次树形背包DP(当前枚举到了uu的子节点vv,f′f′表示枚举到子节点vv之前的DP值):
f[u][i]=∑j,kf′[u][j]×f[v][k]×ORZf[u][i]=∑j,kf′[u][j]×f[v][k]×ORZ
ORZORZ表示jj段和kk段合并成ii段的方案数。
如何求ORZORZ呢?
设f[u]f[u]的质量序列为AA,f′[u]f′[u]的质量序列为BB,f[v]f[v]的质量序列为CC。
AA中的每一段可以只包含BB中的一段,可以只包含CC中的一段,也可以有BB和CC中各一段合并而成,但不能为空。特殊地,AA的第一段只能包含节点uu。
相当于先枚举BB中的j−1j−1段在AA中放的位置,方案数为Cj−1i−1Ci−1j−1,然后把CC中的i−ji−j段放到AA中剩下的位置,使每一段都不为空。现在CC中还剩下k−i+jk−i+j个段,他们需要与BB中的段合并,方案数Ck−i+jj−1Cj−1k−i+j。
所以:
ORZ=Cj−1i−1×Ck−i+jj−1ORZ=Ci−1j−1×Cj−1k−i+j
最后答案∑if[n+1][i]∑if[n+1][i]。
复杂度:f[u][i]f[u][i]第二维的上界只有uu的子树大小,枚举ii相当于枚举ii的子树内的点。所以看上去是O(n4)O(n4)的,实际上每对点都只在lca处被计算贡献了O(n)O(n)次,复杂度O(n3)O(n3)。
代码:
对每张图片ii,小D都最多只记住了某一张质量不比ii差的另一张图片KiKi。
缩点后建树,对于每个ii,如果KiKi存在,就将KiKi作为ii的父节点。建树后有可能是一棵森林,所以新建一个新的节点n+1n+1连接森林里每棵树的根节点,形成一棵树,n+1n+1为根。
然后树形DP,f[u]f[u]表示uu的子树内的方案数。
但对于uu的两个不同子节点vv和ww,vv和ww的子树内可能存在两个点质量相等,所以还需要加一维:
f[u][i]f[u][i]表示uu的子树里,分成ii段(也就是共有i−1i−1个小于号把质量序列分成了ii个部分,每个部分里的图片质量相等)的方案数,然后做一次树形背包DP(当前枚举到了uu的子节点vv,f′f′表示枚举到子节点vv之前的DP值):
f[u][i]=∑j,kf′[u][j]×f[v][k]×ORZf[u][i]=∑j,kf′[u][j]×f[v][k]×ORZ
ORZORZ表示jj段和kk段合并成ii段的方案数。
如何求ORZORZ呢?
设f[u]f[u]的质量序列为AA,f′[u]f′[u]的质量序列为BB,f[v]f[v]的质量序列为CC。
AA中的每一段可以只包含BB中的一段,可以只包含CC中的一段,也可以有BB和CC中各一段合并而成,但不能为空。特殊地,AA的第一段只能包含节点uu。
相当于先枚举BB中的j−1j−1段在AA中放的位置,方案数为Cj−1i−1Ci−1j−1,然后把CC中的i−ji−j段放到AA中剩下的位置,使每一段都不为空。现在CC中还剩下k−i+jk−i+j个段,他们需要与BB中的段合并,方案数Ck−i+jj−1Cj−1k−i+j。
所以:
ORZ=Cj−1i−1×Ck−i+jj−1ORZ=Ci−1j−1×Cj−1k−i+j
最后答案∑if[n+1][i]∑if[n+1][i]。
复杂度:f[u][i]f[u][i]第二维的上界只有uu的子树大小,枚举ii相当于枚举ii的子树内的点。所以看上去是O(n4)O(n4)的,实际上每对点都只在lca处被计算贡献了O(n)O(n)次,复杂度O(n3)O(n3)。
代码:
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; inline int read() { int res = 0; bool bo = 0; char c; while (((c = getchar()) < '0' || c > '9') && c != '-'); if (c == '-') bo = 1; else res = c - 48; while ((c = getchar()) >= '0' && c <= '9') res = (res << 3) + (res << 1) + (c - 48); return bo ? ~res + 1 : res; } inline char get() { char c; while ((c = getchar()) != '<' && c != '='); return c; } const int N = 135, M = 265, ZZQ = 1e9 + 7; int n, m, X , Y , fa , ecnt, nxt[M], adj , go[M], in , cnt , f , sze , C , g ; bool eq , its ; void init() { int i, j; for (i = 0; i <= 120; i++) C[i][0] = 1; for (i = 1; i <= 120; i++) for (j = 1; j <= i; j++) C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % ZZQ; } void add_edge(int u, int v) { nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v; nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u; } int cx(int x) { if (fa[x] != x) fa[x] = cx(fa[x]); return fa[x]; } bool zm(int x, int y) { int ix = cx(x), iy = cx(y); if (ix != iy) fa[iy] = ix; else return 1; return 0; } void dfs(int u, int fu) { int i, j, k; sze[u] = f[u][1] = 1; for (int e = adj[u], v; e; e = nxt[e]) { if ((v = go[e]) == fu) continue; dfs(v, u); for (i = 1; i <= n; i++) g[i] = 0; for (i = 1; i <= sze[u] + sze[v]; i++) for (j = 1; j <= sze[u]; j++) for (k = 1; k <= sze[v]; k++) { int x = k - i + j; if (x < 0) continue; (g[i] += 1ll * f[u][j] * f[v][k] % ZZQ * C[i - 1][j - 1] % ZZQ * C[j - 1][x] % ZZQ) %= ZZQ; } for (i = 1; i <= sze[u] + sze[v]; i++) f[u][i] = g[i]; sze[u] += sze[v]; } } int main() { int i; n = read(); m = read(); init(); for (i = 1; i <= n; i++) fa[i] = i; for (i = 1; i <= m; i++) X[i] = read(), eq[i] = get() == '=', Y[i] = read(); for (i = 1; i <= m; i++) if (eq[i]) zm(X[i], Y[i]); for (i = 1; i <= n; i++) its[in[i] = cx(i)] = 1; for (i = 1; i <= n; i++) fa[i] = i; for (i = 1; i <= m; i++) if (!eq[i]) { add_edge(in[X[i]], in[Y[i]]); cnt[in[Y[i]]]++; if (zm(in[X[i]], in[Y[i]])) return printf("0\n"), 0; } for (i = 1; i <= n; i++) if (its[i] && !cnt[i]) add_edge(n + 1, i); int ans = 0; dfs(n + 1, 0); for (i = 1; i <= sze[n + 1]; i++) ans = (ans + f[n + 1][i]) % ZZQ; cout << ans << endl; return 0; }
相关文章推荐
- BZOJ 4013 HNOI2015 实验比较 树形DP+组合数学
- bzoj 4013: [HNOI2015]实验比较 (树形DP+组合数学)
- 【bzoj4013】[HNOI2015]实验比较 树形dp+组合数学
- [BZOJ4013][HNOI2015]实验比较(树形DP)
- [树形DP] BZOJ 4013 [HNOI2015]实验比较
- [BZOJ4013][HNOI2015]实验比较-树形DP
- bzoj 4013: [HNOI2015]实验比较 树形dp+排列组合
- 【BZOJ 4013】[HNOI2015]实验比较
- BZOJ 4013 【HNOI2015】 实验比较
- BZOJ 4013: [HNOI2015]实验比较
- 【BZOJ】4013: [HNOI2015]实验比较
- bzoj 4013: [HNOI2015]实验比较
- BZOJ4013 : [HNOI2015]实验比较
- bzoj 4013: [HNOI2015]实验比较
- BZOJ4013 [HNOI2015]实验比较
- 【bzoj4013】 HNOI2015—实验比较
- BZOJ 4013: [HNOI2015]实验比较
- 4013: [HNOI2015]实验比较
- [HNOI 2015]实验比较
- BZOJ4013: [HNOI2015]实验比较