首页 > 其他分享 >P10013 [集训队互测 2023] Tree Topological Order Counting

P10013 [集训队互测 2023] Tree Topological Order Counting

时间:2024-08-29 23:14:31浏览次数:10  
标签:sz le kMod int P10013 Tree Topological leq kMaxN

Description

给定一颗 \(n\) 个点的有根树,\(1\) 是根,记 \(u\) 的父亲是 \(fa_u\)。另给出一长度为 \(n\) 的权值序列 \(b\)。

称一个长度为 \(n\) 的排列 \(a\) 为这颗树的合法拓扑序,当且仅当 \(\forall 2 \le u \le n,a_u > a_{fa_u}\)。

对每个点 \(u\),定义 \(f(u)\) 为,在所有这颗树的合法拓扑序中,\(b_{a_u}\) 之和。

现在对 \(1 \le u \le n\),求 \(f(u) \bmod 10^9+7\)。

\(2 \le n \le 5000\),\(1 \le fa_i < i\),\(0 \le b_i < 10^9+7\)。

Solution

先考虑对于每个 \(x\) 怎么求出答案。

假设当前处理到了节点 \(u\)(要求 \(u\) 是 \(x\) 的祖先),\(v\) 为 \(u\) 的儿子中子树包含 \(x\) 的儿子。

设 \(f_u\) 表示 \(u\) 的子树的合法拓扑序数量,\(g_{u,i}\) 表示 \(u\) 的子树里已经钦定恰好有 \(i\) 个点 dfs 序小于 \(x\) 的方案数,\(h_u\) 表示 \(u\) 的子树去掉包含 \(x\) 的子树剩下的点的合法拓扑序数。可以得到转移:

\[\begin{aligned} f_u&=(sz_u-1)!\prod_{w\in \text{son}_u}{f_w/(sz_w!)}\\ h_u&=\prod_{w\in \text{son}_u,w\neq v}{f_w/(sz_w!)}\\ g_{u,i+j+1}&=\sum_{0\leq i\leq sz_v-1,0\leq j\leq sz_u-sz_v-1}{\binom{i+j}{j}\binom{sz_u-i-j-2}{sz_v-i-1}h_ug_{v,i}} \end{aligned} \]

最终的答案即为 \(\sum_{i=1}^{n}{g_{1,i-1}b_i}\)。

时间复杂度:\(O(n^3)\)。


考虑怎么优化。

容易发现这题主要慢在每次要处理从一个点到根的路径上的 dp,而不是从根到某个点的路径,这样会导致每个点之间的信息没有任何交集。

注意到这题转移的终点是一定的,就是根,而起点不一样。所以可以类似这题的思路把转移倒过来做。

具体的,将 \(g_{u,i}\) 的状态改为当前状态为 \((u,i)\),到最终状态对答案的贡献。转移改为:

\[g_{v,i}=\sum_{0\leq i\leq sz_v-1,0\leq j\leq sz_u-sz_v-1}{\binom{i+j}{j}\binom{sz_u-i-j-2}{sz_v-i-1}h_ug_{u,i+j+1}} \]

最终 \(x\) 的答案就是 \(f_xg_{x,0}\)。

时间复杂度:\(O(n^2)\)。

Code

#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 5e3 + 5, kMod = 1e9 + 7;

int n;
int a[kMaxN], C[kMaxN][kMaxN], sz[kMaxN], f[kMaxN], g[kMaxN][kMaxN];
std::vector<int> G[kMaxN];

constexpr int qpow(int bs, int64_t idx = kMod - 2) {
  int ret = 1;
  for (; idx; idx >>= 1, bs = (int64_t)bs * bs % kMod)
    if (idx & 1)
      ret = (int64_t)ret * bs % kMod;
  return ret;
}

inline int add(int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); }
inline int sub(int x, int y) { return (x >= y ? x - y : x - y + kMod); }
inline void inc(int &x, int y) { (x += y) >= kMod ? x -= kMod : x; }
inline void dec(int &x, int y) { (x -= y) < 0 ? x += kMod : x; }

void prework() {
  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] = add(C[i - 1][j], C[i - 1][j - 1]);
  }
}

void dfs1(int u) {
  f[u] = 1, sz[u] = 0;
  for (auto v : G[u]) {
    dfs1(v);
    f[u] = 1ll * f[u] * f[v] % kMod * C[sz[u] += sz[v]][sz[v]] % kMod;
  }
  ++sz[u];
}

void dfs2(int u) {
  for (auto v : G[u]) {
    int coef = 1, now = 0;
    for (auto w : G[u]) {
      if (w != v) coef = 1ll * coef * f[w] % kMod * C[now += sz[w]][sz[w]] % kMod;
    }
    for (int i = 0; i <= sz[v] - 1; ++i)
      for (int j = 0; j <= sz[u] - sz[v] - 1; ++j)
        inc(g[v][i], 1ll * coef * C[i + j][j] % kMod * C[sz[u] - i - j - 2][sz[v] - i - 1] % kMod * g[u][i + j + 1] % kMod);
    dfs2(v);
  }
}

void dickdreamer() {
  std::cin >> n;
  for (int i = 2; i <= n; ++i) {
    int p;
    std::cin >> p;
    G[p].emplace_back(i);
  }
  for (int i = 1; i <= n; ++i) std::cin >> a[i];
  prework();
  for (int i = 1; i <= n; ++i) g[1][i - 1] = a[i];
  dfs1(1), dfs2(1);
  for (int i = 1; i <= n; ++i) std::cout << 1ll * f[i] * g[i][0] % kMod << ' ';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}

标签:sz,le,kMod,int,P10013,Tree,Topological,leq,kMaxN
From: https://www.cnblogs.com/Scarab/p/18387709

相关文章

  • Spark MLlib模型训练—回归算法 Decision tree regression
    SparkMLlib模型训练—回归算法Decisiontreeregression在机器学习中,决策树是一种常用且直观的模型,广泛应用于分类和回归任务。决策树回归(DecisionTreeRegression)通过将数据集分割成多个区域,构建一棵树形结构,以预测目标变量的连续值。本文将详细探讨Spark中的决......
  • AT cf17 final J Tree MST
    ATcf17finalJTreeMST考场上想出的黑题,然而写挂了……思路考场推出boruvka算法,会的直接跳过就好。结论:一个点向另外一个点连出的最小边,一定在最小生成树上。证明:参考Kruskal生成树的流程,若当前边(最小边)不在最小生成树上,表明边的两端已经在同一个连通块中。那么存在一......
  • 分析 HashSet 和 TreeSet 分别如何实现去重的
     分析HashSet和TreeSet分别如何实现去重的: (1)HashSet的去重机制:hashCode()+equals()。底层先通过存入对象,进行运算得到一个hash值,通过hash值得到对应的索引,如果发现table索引所在的位置,没有数据,就直接存放;如果有数据,就进行equals遍历比较,比较后不相同,就加入,否......
  • VBA学习(60):补充:Excel VBA 选择输入/TreeView控件/在工作表中如何顺利使用TreeView控
    上一篇文章我们分享了一例通过TreeView控件,实现会计科目的选择输入的方法,(ExcelVBA选择输入/TreeView控件):然而,当今天我打开它准备发送给索要示例文件的小伙伴的时候,咦,这是什么鬼?再进入设计模式:TreeView1这个控件,它在啊在代码窗口查看:名称怎么变成了TreeView41?难......
  • 简单萌萌哒 Top Tree(上)
    前情提要。TopCluster分解与TopTree情景导入我们总是想要以一种合适的方式对树进行划分,但是对于菊花图而言,基于点的划分总是不合适的,这启发我们基于边进行划分。事实上可以证明,基于边的划分总是可行的。TopCluster分解就是一种基于边的划分方式,下面我们来介绍他。簇定......
  • Lanelet2与OpenDrive和OpenStreetMap的关系
    Lanelet2、OpenDrive和OpenStreetMap在自动驾驶和智能交通系统中都扮演着重要角色,但它们之间在功能和用途上存在一些差异。以下是它们之间关系的详细阐述:Lanelet2定义与功能:Lanelet2是一个专为自动驾驶和智能交通系统设计的高精度道路网络表示框架。它提供了丰富的数据结......
  • WPF LogicalTree vs Visual Tree
    Copyfrom https://www.c-sharpcorner.com/blogs/wpf-logical-and-visual-trees1  WPF'shierarchicalstructurerequiresanewconceptualmodelofapplicationstructure,whichtakestheformofanelementtree.Twotypesofelementtreesarerequiredt......
  • Minimum Steiner Tree 题解
    原题,详见P10723。几乎相同,我们只需要以一个需要选择的点为根,遍历子树看看有没有出现需要选择的点,然后直接去删除即可,可以看我的博客。但是我们也可以换一种做法,用类似拓扑排序的算法。先找到所有只连一条边且没有被选择的点,然后放进队列,接着依次取出队头遍历与它相连的点,同时记......
  • 树上启发式合并——dsu on tree
    参考文章:树上启发式合并[dsuontree]树上启发式合并总结树上启发式合并の详解启发式合并启发式算法是什么呢?启发式算法是基于人类的经验和直观感觉,对一些算法的优化。举个例子,最常见的就是并查集的启发式合并了,代码是这样的:voidmerge(intx,inty){intxx=find(x......
  • ABC 368D Minimum Steiner Tree
    题意给你一颗由N个点组成的树,指定K个节点,求包含这K个节点的最小子树的大小思路考虑正难则反,我们从开始的树当中剪掉那些没有任何指定点的子树,剩下来的子树就是最小的、能包含所有指定节点的子树。关于剪去这个操作,就是dfs一旦遇到以当前节点为根的子树没有任何指定点时,就停止df......