首页 > 其他分享 >CF762F Tree nesting

CF762F Tree nesting

时间:2023-10-28 14:45:18浏览次数:30  
标签:std 匹配 int auto Tree CF762F nesting ans mod

来一点更清楚的、实现方面的东西。

做法同 这篇,他的实现很优美但略微繁琐了些。

枚举 \(T\) 的形态,发现这个匹配不过是把每个 \(T\) 中当前点的儿子塞进一个 \(S\) 中当前点的儿子内。于是 \(f_{u, v}\) 表示 \(S\) 中 \(u\) 匹配 \(T\) 中 \(v\) 且 \(v\) 整棵子树被匹配完的方案数。发现转移时必须知道哪些 \(v\) 的儿子还需要匹配一个 \(u\) 的儿子,于是转移时开辅助数组 \(g_{msk}\) 表示 \(msk\) 代表的 \(v\) 儿子集合已经匹配。

转移顺序大致是:枚举 \(T\) 的根并清空 \(f\)(因为这是记搜) -> 枚举 \(u\),这需要一个 dfs,因为需要知道 \(u\) 的父亲 -> 枚举 \(u\) 的儿子、枚举 \(v\) 的儿子,进行匹配。

后面那个匹配是:\(g_{S} f_{su, sv} \to g_{S \cup \{sv\}}\),\(su\) 是 \(s\) 的一个儿子,\(sv\) 是 \(v\) 的一个儿子。\(g_{son(v)}\) 即 \(f_{u, v}\),注意这里的 \(g\) 是为了求 \(f_{u, v}\) 开的。

注意到这个匹配是一个分组背包,可以开辅助数组辅助 \(g\) 的转移,也可以从大到小枚举 msk 保证已经被更新过的状态不更新其余状态。这里写了第二种。

对于 \(S\) 的一张子图出现多种匹配方式,算出 \(T\) 与自己有标号匹配的方法数,除掉 \(S\) 有标号匹配方法数,就是 \(S\) 无标号匹配方法数。

复杂度是 \(O(n m^2 2^m)\),跑不满,因为那个 \(m 2^m\) 本质上是 \(\sum d 2^d\),\(d\) 是度数。

不大懂为什么要把儿子的匹配情况记进状态里。这道题挺自然,做起来与写起来都挺舒服。

#include <bits/stdc++.h>

const int mod = 1e9 + 7;

using tree = std::vector<std::vector<int>>;

int qpow(int a, int b) {
  int ans(1);
  for (; b; b >>= 1) {
    if (b & 1) ans = 1ll * ans * a % mod;
    a = 1ll * a * a % mod;
  }
  return ans;
}

void add(int &x, int y) { if ((x += y) >= mod) x -= mod; }

int main() {
  int n, m; scanf("%d", &n);
  std::vector<std::vector<int>> s(n), t;
  for (int i = 1, u, v; i < n; i++) {
    scanf("%d %d", &u, &v), --u, --v;
    s[u].push_back(v), s[v].push_back(u);
  }
  scanf("%d", &m), t.resize(m);
  for (int i = 1, u, v; i < m; i++) {
    scanf("%d %d", &u, &v), --u, --v;
    t[u].push_back(v), t[v].push_back(u);
  }
  bool o = 0;
  auto match = [&](tree s, tree t) -> int {
    std::vector<std::vector<int>> f(n, std::vector<int> (m, -1));
    // S 中 u 匹配 T 中 v 的方案数,另外两参数是父亲
    auto dfs = [&](auto self, int u, int fu, int v, int fv) -> int {
      if (f[u][v] != -1) return f[u][v];
      int k = (int) t[v].size() - (fv == -1 ? 0 : 1);
      std::vector<int> g(1 << k);
      g[0] = 1;
      for (auto su : s[u]) if (su != fu) {
        std::vector<int> tmp(k);
        int cnt = 0;
        for (auto sv : t[v]) if (sv != fv) 
          tmp[cnt++] = self(self, su, u, sv, v);
        for (int i = (1 << k) - 1; i >= 0; i--) if (g[i])
          for (int j = 0; j < k; j++) if (!(i & (1 << j))) {
            add(g[i | (1 << j)], 1ll * g[i] * tmp[j] % mod);
        }
      }
      return f[u][v] = g[(1 << k) - 1];
    } ;
    int sum = 0;
    for (int i = 0; i < m; i++) {
      f = std::vector<std::vector<int>> (n, std::vector<int> (m, -1));
      // T 以 i 为根,i 匹配 S 中 u 的方案数
      auto solve = [&](auto self, int u, int fa) -> int {
        int ans = dfs(dfs, u, fa, i, -1);
        for (auto v : s[u]) if (v != fa) 
          add(ans, self(self, v, u));
        return ans;
      } ;
      add(sum, solve(solve, 0, -1));
    }
    return sum;
  } ;
  printf("%d\n", 1ll * match(s, t) * qpow(match(t, t), mod - 2) % mod);
  return 0;
}

标签:std,匹配,int,auto,Tree,CF762F,nesting,ans,mod
From: https://www.cnblogs.com/purplevine/p/solution-cf762f.html

相关文章

  • CF375E Red and Black Tree
    看错题看成只能交换相邻节点颜色了/fn每次操作交换两个节点颜色,可以转化为统计最终合法颜色序列相比开始,最少有多少个红点变成黑点。可以考虑一个类似树形dp的过程,对于每个节点我们钦定下它会被哪个节点“笼罩”,同时由于黑点数量有限,我们还需要记录下子树内已经用了多少个黑点......
  • 循序渐进介绍基于CommunityToolkit.Mvvm 和HandyControl的WPF应用端开发(5) -- 树列表
    在我们展示一些参考信息的时候,有所会用树形列表来展示结构信息,如对于有父子关系的多层级部门机构,以及一些常用如字典大类节点,也都可以利用树形列表的方式进行展示,本篇随笔介绍基于WPF的方式,使用TreeView来洗实现结构信息的展示,以及对它的菜单进行的设置、过滤查询等功能的实现逻辑......
  • A Tour Through TREE_RCU's Data Structures (翻译)
    原文:https://www.kernel.org/doc/html/latest/RCU/Design/Data-Structures/Data-Structures.htmlDecember18,2016ThisarticlewascontributedbyPaulE.McKenneyIntroductionThisdocumentdescribesRCU'smajordatastructuresandtheirrelationshiptoea......
  • AtCoder Regular Contest 164 F Subtree Reversi
    洛谷传送门AtCoder传送门非常好题目。发现每个点颜色被反转的次数是固定的,为其深度(根结点深度为\(0\))。于是可以看作是,一放棋子就得到分数。那么先手取偶数层和后手取奇数层都会使先手得分,所以双方的目标都是尽可能多取偶数层的结点。考虑若一开始有偶数层的叶子,那么当前的......
  • CF868E Policeman and a Tree
    感觉,好自然啊!想法dp,想办法分解这个博弈的过程。发现警察会从一片叶子到另一片叶子,在叶子抓住小偷时所有小偷可以全树乱走。因此dp:\(f_{u,i}\)表示警察位于\(u\),全树剩余\(i\)个小偷时的答案。因为两边都绝对理性,小偷在警察离开叶子后不会移动并位于多片叶子上。考虑小偷......
  • P6109 [Ynoi2009] rprmq1 题解-猫树+Segment Tree Beats
    20231025P6109[Ynoi2009]rprmq1题解-猫树+SegmentTreeBeats不愧是学长出的题。。。让我更深刻地理解了猫树。Statement传送门有一个\(n\timesn\)的矩阵\(a\),初始全是\(0\),有\(m\)次修改操作和\(q\)次查询操作,先进行所有修改操作,然后进行所有查询操作。一次修......
  • 75th 2023/10/6 k-D Tree
    附上一图:按维度分级,每次轮换用哪个维度即可oi中大多为2维这就是我对它的全部理解了结构与线段树几乎相同分左右结点时取当前区间段的中位数因而每一个节点都不同于线段树的表示范围它表示的是一个确确实实的节点的值访问前可以维护一个节点及它的子树的维度上下界以减少询......
  • CF1572F Stations 题解-Segment Tree Beats
    20231025CF1572FStations题解-SegmentTreeBeats吉司机线段树好题!!!CF3400。传送门Statement有\(n\)个广播站,第\(i\)个广播站高度为\(h_i\),范围为\(w_i\)。初始\(h_i=0,w_i=i\)。广播站\(i\)能向广播站\(j\)传递消息,当且仅当\(i\lej\lew_i\),且\(h_i>\max\lim......
  • CF1467E Distinctive Roots in a Tree
    突然发现深究一些树上问题还是挺有意思的哈。显然对于同一种权值的任意两个结点,其两端的部分都是不合法的。维护两个标记表示子树内均不合法与子树外均不合法即可。但相同权值的点对数量是\(O(n^2)\)的,我们要优化这个过程。发现很多点对都是无用的。DFS下去,遇到一个\(x\)......
  • Jquery 下拉树下面是一个使用Combotree组件的简单案例:
    1、html <!DOCTYPEhtml><html><head><title>Combotree使用案例</title><linkrel="stylesheet"type="text/css"href="https://cdn.jsdelivr.net/npm/jquery-combotree/dist/jquery.combotree.min.css"......