[ARC185D] Random Walk on Tree 题解

一个很套路的做法。

思路#

题目要求走完整个树的时间,这并不好算,容易想到 min-max 容斥。

依据 min-max 容斥,我们可以轻松把它转化成第一次走到所有子集的时间。

考虑在这道题中,有什么特殊的。

第一,任何包含根节点的子集答案都是零。

第二,由于我们只关心第一次走到的点的时间,因此假如一个点的祖先被选中,那么这个点是否选中是无关紧要的。

这些无关紧要的点地位相同,所以我们可以一起考虑。

假设有 n 个点是无关紧要的。

它们对答案的贡献是什么呢?

是:

(1)ki=0n(1)i(ni)

这是非常令人熟悉的二项式定理。

因此:

(1)ki=0n(1)i(ni)=(1)k[n=0]

我们发现,只有当 n=0 的时候才对答案有贡献。

n=0 的时候,每一条链只有最底部的点可能被选。

现在计算这样的时间即可。

首先,对于底部被选的链而言。

我们设 fi 表示到达深度为 i 的点还要走的期望步数,根节点的深度为 0

那么有:

fm=0fm1=(fm+fm2)2+1=fm22+1fm2=fm3+fm12+1=fm3+fm22+12+1=2fm33+2fmi=ifmi1i+1+i

对于底部没有被选的链而言。

有:

fn=fm1+1fm1=(fn+fm2)2+1=fm2+3fmi=fmi+2×i+1

所有期望都可以推到根上。

因此,假设有 i 条链被选,则有 (ni) 条链没有被选:

f0=i((m1)f0m+m1)+(ni)(f0+2×m1)n+1

可以继续化简。

f0=i((m1)f0m+m1)+(ni)(f0+2×m1)n+1inf0=i((m1)f0m+m1)+(ni)(2×m1)n+1(ini(m1)nm)f0=i(m1)+(ni)(2×m1)n+1(ii(m1)m)f0=i(m1)+(ni)(2×m1)+nimf0=i(m1)+(ni)(2×m1)+n

算出 f0 后,不要忘记乘负一和组合数的系数。

时间复杂度:O(nlogmod)

瓶颈在求逆元,可以线性预处理,但没有必要。

Code#

/*
  ! 前途似海,来日方长。
  ! Created: 2024/10/13 21:48:58
*/
#include <bits/stdc++.h>
using namespace std;

#define x first
#define y second
#define int long long
#define mp(x, y) make_pair(x, y)
#define eb(...) emplace_back(__VA_ARGS__)
#define fro(i, x, y) for (int i = (x); i <= (y); i++)
#define pre(i, x, y) for (int i = (x); i >= (y); i--)
inline void JYFILE19();

using i64 = long long;
using pii = pair<int, int>;

bool ST;
const int N = 1e6 + 10;
const int mod = 998244353;

int n, m;

namespace Math {
int Fc[1000010], Iv[1000010];
template<typename T> inline void add(T&x, int y) { if ((x += y) >= mod) x -= mod; }
template<typename T> inline void del(T&x, int y) { if ((x -= y) < 0) x += mod; }
template<typename T> inline void add(T&x, int y, int z) { x = (x + (long long) y * z) % mod; }
template<typename T> inline void mo(T&x) { x = (x % mod + mod) % mod; }
inline long long power(long long x, long long y) {
  long long res = 1;
  while (y) { if (y & 1) res = res * x % mod; x = x * x % mod, y /= 2; }
  return res;
}
inline void init(int n) {
  Fc[0] = 1;
  for (int i = 1; i <= n; i++) Fc[i] = (long long) Fc[i - 1] * i % mod; Iv[n] = power(Fc[n], mod - 2);
  for (int i = n; i >= 1; i--) Iv[i - 1] = (long long) Iv[i] * i % mod;
}
inline long long C(int x, int y) { return (x < 0 || y < 0 || x < y ? 0 : (long long) Fc[x] * Iv[y] % mod * Iv[x - y] % mod); }
} using namespace Math;

signed main() {
  JYFILE19();
  cin >> n >> m, init(n);
  int ns = 0;
  fro(i, 1, n) {
    int x = C(n, i);
    int up = (i * (m - 1) + (n - i) * (2 * m - 1)) % mod;
    int dw = (i * power(m, mod - 2)) % mod;
    int sm = ((up + n) * power(dw, mod - 2)) % mod;
    ns = (ns + (i & 1 ? 1 : -1) * x * sm) % mod;
  }
  cout << (ns % mod + mod) % mod << "\n";
  return 0;
}

bool ED;
inline void JYFILE19() {
  // freopen(".in", "r", stdin);
  // freopen(".out", "w", stdout);
  srand(random_device{}());
  ios::sync_with_stdio(0), cin.tie(0);
  double MIB = fabs((&ED - &ST) / 1048576.), LIM = 32;
  cerr << "MEMORY: " << MIB << endl, assert(MIB <= LIM);
}

作者:JiaY19

出处:https://www.cnblogs.com/JiaY19/p/18475000

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   JiaY19  阅读(5)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示