首页 > 其他分享 >CF512D Fox And Travelling 题解

CF512D Fox And Travelling 题解

时间:2024-07-22 23:08:43浏览次数:9  
标签:kMod sz int 题解 bs kMaxN ret CF512D Travelling

Description

  • 给定一张 \(n\) 个点 \(m\) 条边的无向图。
  • 一个点只有当与它直接相连的点中最多只有一个点未被选择过时才可被选择。
  • 询问对于每个 \(k \in [0,n]\),有序选择 \(k\) 个点的方案数。
  • \(n \le 100\),\(m \le \frac{n(n-1)}2\),答案对 \(10^9+9\) 取模。

Solution

容易发现一个环上的任意一个点一定不能被选,因为只要不动这些点那么它们一定不会变,也就永远没选。并且如果两个环之间存在一条路径,则这个路径上的也不能选。

用拓扑排序去掉这些不能选的点后原图就变为一个森林,并且有两类点:周围有不能删的和周围没有不能删的。

容易发现一棵树里最多有一个周围不能删的,如果有则这个点一定是最后一个选,就把这个点作为根,跑 dp。

设 \(f_{i,j}\) 表示以 \(i\) 为根的子树里选 \(j\) 个点的方案数,容易 \(O(n^2)\) 转移。

由于 \(i\) 要选的话一定是最后一个,所以 \(f_{i,sz_i}=f_{i,sz_i-1}\)。


考虑树没有根的情况。

先让每个点作为根求一遍答案加起来,但是会有算重,容易发现对于一个大小为 \(i\) 的方案,会在不在这 \(i\) 个点里的点作为根时被算到,所以会算 \(sz-i\) 次,除去这个就行了。特别的对于 \(i=sz\) 则显然不会算重,就不用除。

然后类似 dp 过程合并答案即可。

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

Code

#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 105, kMod = 1e9 + 9;

int n, m, rt;
int f[kMaxN][kMaxN], g[kMaxN], tmp[kMaxN], ans[kMaxN], sz[kMaxN], C[kMaxN][kMaxN];
bool vis[kMaxN], del[kMaxN], exi[kMaxN];
std::vector<int> G[kMaxN], vec;

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 _dfs(int u, int fa, int rt) {
  vis[u] = 1;
  for (auto v : G[u]) {
    if (v == fa) continue;
    if (!vis[v])
      _dfs(v, u, rt);
    else
      del[rt] |= (v == rt);
  }
}

void check(int x) {
  std::fill_n(vis + 1, n, 0);
  _dfs(x, 0, x);
  exi[x] = del[x];
}

void dfs1(int u, int fa) {
  vec.emplace_back(u);
  exi[u] = 1;
  bool fl = 0;
  for (auto v : G[u]) {
    fl |= del[v];
    if (v == fa || exi[v]) continue;
    dfs1(v, u);
  }
  if (fl) {
    if (~rt && !rt)
      rt = u;
    else if (~rt)
      rt = -1;
  }
}

void dfs2(int u, int fa) {
  sz[u] = 0, f[u][0] = 1;
  for (auto v : G[u]) {
    if (v == fa || del[v]) continue;
    dfs2(v, u);
    std::fill_n(g, n + 1, 0);
    for (int i = 0; i <= sz[u]; ++i)
      for (int j = 0; j <= sz[v]; ++j)
        inc(g[i + j], 1ll * f[u][i] * f[v][j] % kMod * C[i + j][i] % kMod);
    sz[u] += sz[v];
    for (int i = 0; i <= sz[u]; ++i) f[u][i] = g[i];
  }
  ++sz[u];
  f[u][sz[u]] = f[u][sz[u] - 1];
}

void dickdreamer() {
  std::cin >> n >> m;
  for (int i = 1; i <= m; ++i) {
    int u, v;
    std::cin >> u >> v;
    G[u].emplace_back(v), G[v].emplace_back(u);
  }
  prework();
  for (int i = 1; i <= n; ++i) check(i);
  ans[0] = 1;
  for (int i = 1; i <= n; ++i) {
    if (!exi[i]) {
      vec.clear();
      rt = 0, dfs1(i, 0);
      if (!~rt) continue;
      if (rt) {
        dfs2(rt, 0);
        std::fill_n(g, n + 1, 0);
        for (int j = 0; j <= n; ++j) {
          for (int k = 0; k <= n - j; ++k)
            inc(g[j + k], 1ll * f[rt][j] * ans[k] % kMod * C[j + k][j] % kMod);
        }
        for (int j = 0; j <= n; ++j) ans[j] = g[j];
      } else {
        std::fill_n(tmp, n + 1, 0);
        for (auto x : vec) {
          dfs2(x, 0);
          for (int j = 0; j <= n; ++j)
            inc(tmp[j], f[x][j]);
        }
        std::fill_n(g, n + 1, 0);
        for (int j = 0; j <= (int)vec.size(); ++j) {
          tmp[j] = 1ll * tmp[j] * (j == vec.size() ? 1 : qpow((int)vec.size() - j)) % kMod;
          for (int k = 0; k <= n - j; ++k)
            inc(g[j + k], 1ll * tmp[j] * ans[k] % kMod * C[j + k][j] % kMod);
        }
        for (int j = 0; j <= n; ++j) ans[j] = g[j];
      }
    }
  }
  for (int i = 0; i <= n; ++i) std::cout << ans[i] << '\n';
}

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;
}

标签:kMod,sz,int,题解,bs,kMaxN,ret,CF512D,Travelling
From: https://www.cnblogs.com/Scarab/p/18317203

相关文章

  • [COCI2015-2016#1] UZASTOPNI 题解
    前言题目链接:洛谷。题意简述一棵有根树,节点数\(n\leq10^5\),每个点有权值\(v_i\leq2000\),现在选出一些点,满足:一个点的父亲点若未被选择则其不能被选择。所选点的集合内不能有相同的权值。对于每一个选择的点,其子树中所有被选择点的权值必须可以构成公差为\(1\)的等......
  • 2024牛客暑期多校训练营2(部分题目题解)
    2024牛客暑期多校训练营2(部分题目题解)C.RedWalkingonGrid题意:给定只有红白的2*n个格子,只能走红色各自且只能上下左右走,问最多可以走多少红色格子。题解:左右走:dp[0][i]=dp[0][i-1]+1;上下走:intk1=dp[0][i];intk2=dp[1][i];dp[0][i]=max(dp[0][i],k2+......
  • 20240722题解
    孩子们,我回来了......
  • Luogu P4310 绝世好题 题解 [ 绿 ] [ 线性 dp ] [ 单调队列优化 ] [ 二进制优化 ]
    题目:绝世好题。暴力dp显然\(O(n^2)\)转移即可。单调队列优化观察到只有某二进制位两个数都为\(1\)时才能转移,因此我们把每个二进制位开一个单调队列,然后对于一个数\(a\),找到它是\(1\)的二进制位并选其单调队列的队头进行转移,在这之后把这个数加入符合要求的单调队列......
  • 题解:CF1349B Orac and Medians
    洛谷|CF刷一些CF2000,进行一个录的记。思路记录首先观察到数列里的数不能凭空产生,所以初始序列必须含\(k\)。由于两个数的中位数是较小的那个,所以只要有一个与数列里的\(k\)相邻且比\(k\)大的数,就可以扩展到整个序列。发现可以把第二条推广一下,不必要和\(k\)相邻,因......
  • SLF4J: Class path contains multiple SLF4J bindings 问题解决
    背景:springboot项目名称test,在使用slf4j后,服务启动报错 报错信息:SLF4J:ClasspathcontainsmultipleSLF4Jbindings.SLF4J:Foundbindingin[jar:file:/D:/Program%20Files/Java/.m2/repository/ch/qos/logback/logback-classic/1.2.7/logback-classic-1.2.7.jar!/or......
  • [CEOI2018] Lottery 题解
    前言题目链接:洛谷。题意简述给出序列\(a_1\ldotsa_n\)和常数\(l\leqn\),定义:\[\operatorname{dis}(i,j)=\sum_{k=0}^{l-1}[a_{i+k}\neqa_{j+k}]\qquad(i,j\in[1,n-l+1])\]每次询问一个\(k\),求对于所有\(i\in[1,n-l+1]\),求\(\sum......
  • [COCI2015-2016#1] RELATIVNOST 题解
    前言题目链接:洛谷。这道题有很多做法,但是模拟赛寄了,故记之。题意简述给你两个长为\(n\)的序列\(A\)和\(B\),和一个常数\(c\leq20\),有\(q\)次修改。每次修改后求:\[\large\sum_{S\subseteq\lbracei\rbrace_{i=1}^{n}\land|S|\geqc}\prod_{x\inS......
  • [ARC176E] Max Vector 题解
    题目链接点击打开链接题目解法这个题的一个转化很关键:把一次操作\(j\)等价看作必须满足\((\forall_{1\lei\len}X_i\gea_{j,i})|(\forall_{1\lei\len}Y_i\gea_{j,i})=1\)正确性是显然的另外的一个关键思路是网络流有了上面的转化,我们考虑切糕模型(其实接下来都好想......
  • 题解 B3694 数列离散化
    link简而言之,离散化就是把一个数列转化为由小到大的排名来缩小范围。离散化需要这个题不用数字本身。举个例子:-200244879914993235793离散化后就是:15243\(-2002\)最小,所以它对应\(1\)\(448799\)最大,所以它对应\(5\)实现考虑如何实现。首先由于离散化前后......