【JZOJ6385】【NOIP2019模拟2019.10.23】B
题目大意
给出一棵树,其中1为根。之后每个点向父亲的父亲再连一条边,求得到的图中,每个点走到1的期望步数(等概率向相邻点走去)。
保证i的父亲fa_i<i。
n\leq 2000
Solution
首先列方程,设f_i表示i走向1的期望步数,有:
f_x=1+\frac{1}{d_x}\sum f_y
其中,d_x是x的度数,y是与x相邻的所有点。
直接高斯消元解方程,复杂度O(n^3),过不了。
观察这个系数矩阵的特点,第x行的系数只会在父亲,父亲的父亲,儿子,儿子的儿子处有值。如果我们从儿子往根消元,每次用第x行消去fa_x行和fa_{fa_x}行的第x列,那么最后会得到一个下三角矩阵。这时第x行的系数只会在父亲,父亲的父亲处有值,我们从根往儿子消元,就能得到对角线矩阵了。
复杂度O(n^2)。这个做法利用了系数矩阵的特点,减少消元次数,真是妙不可言~~~
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2007, P = 998244353;
int n, fa[N], d[N], c[N][N], b[N];
int tot, st[N], nx[N << 2], to[N << 2];
void add(int u, int v) {
to[++tot] = v, nx[tot] = st[u], st[u] = tot;
to[++tot] = u, nx[tot] = st[v], st[v] = tot;
++d[u], ++d[v];
}
int pow(int a, int b) {
int ret = 1;
for (; b; a = 1ll * a * a % P, b >>= 1) if (b & 1) ret = 1ll * ret * a % P;
return ret;
}
int main() {
//freopen("in", "r", stdin);
freopen("b.in", "r", stdin);
freopen("b.out", "w", stdout);
scanf("%d", &n);
for (int i = 2; i <= n; ++i) scanf("%d", &fa[i]), add(i, fa[i]);
for (int i = 1; i <= n; ++i) if (fa[fa[i]]) add(i, fa[fa[i]]);
c[1][1] = 1;
for (int i = 2; i <= n; ++i) {
c[i][i] = 1;
for (int j = st[i]; j; j = nx[j]) c[i][to[j]] = P - pow(d[i], P - 2);
b[i] = 1;
}
for (int i = n; i >= 1; --i) {
if (fa[i]) {
int j = fa[i], res = 1ll * c[j][i] * pow(c[i][i], P - 2) % P;
for (int k = 1; k <= n; ++k) c[j][k] = (c[j][k] - 1ll * c[i][k] * res % P + P) % P;
b[j] = (b[j] - 1ll * b[i] * res % P + P) % P;
}
if (fa[fa[i]]) {
int j = fa[fa[i]], res = 1ll * c[j][i] * pow(c[i][i], P - 2) % P;
for (int k = 1; k <= n; ++k) c[j][k] = (c[j][k] - 1ll * c[i][k] * res % P + P) % P;
b[j] = (b[j] - 1ll * b[i] * res % P + P) % P;
}
}
for (int i = 1; i <= n; ++i) {
for (int l = st[i]; l; l = nx[l]) if (to[l] != fa[i] && to[l] != fa[fa[i]]) {
int j = to[l], res = 1ll * c[j][i] * pow(c[i][i], P - 2) % P;
for (int k = 1; k <= n; ++k) c[j][k] = (c[j][k] - 1ll * c[i][k] * res % P + P) % P;
b[j] = (b[j] - 1ll * b[i] * res % P + P) % P;
}
}
for (int i = 1; i <= n; ++i) printf("%d\n", 1ll * b[i] * pow(c[i][i], P - 2) % P);
return 0;
}
作者:zjlcnblogs
出处:https://www.cnblogs.com/zjlcnblogs/p/11733396.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
分类:
OI
【推荐】博客园的心动:当一群程序员决定开源共建一个真诚相亲平台
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】Flutter适配HarmonyOS 5知识地图,实战解析+高频避坑指南
【推荐】开源 Linux 服务器运维管理面板 1Panel V2 版本正式发布
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 大数据高并发核心场景实战,数据持久化之冷热分离
· 运维排查 | SaltStack 远程命令执行中文乱码问题
· Java线程池详解:高效并发编程的核心利器
· 从“看懂世界”到“改造世界”:AI发展的四个阶段你了解了吗?
· 协程本质是函数加状态机——零基础深入浅出 C++20 协程
· .NET 8 gRPC 实现高效100G大文件断点续传工具
· STM32学会要花费多长时间?一个从机械转行老程序员的血泪史
· LinqPad:C#代码测试学习一品神器
· .NET入行第4年(第二集)
· C#/.NET/.NET Core技术前沿周刊 | 第 43 期(2025年6.16-6.22)