[题解][YZOJ50074] 小 C 的岛屿
仅仅是对 做法的一个记录。
简要题意#
有 座岛屿,初始时没有边。每座岛屿都有一个概率值 和一个大小为 友好列表 。
小 站在 号岛屿,依次执行以下操作:
设现在在岛屿 ,有 的概率产生一条图中尚未存在的随机无向边,不会产生自环。
如果此时所有岛屿仍未联通,她会在当前点的友好列表中,等概率随机选择一个,走到那座岛屿上。并把不满意度增加 ,然后重复 。否则就结束这个过程
求她的期望不满意度,对一个 取模.
.
解题思路#
思考后不难发现,状态只与当前的位置,以及边数相关,那么可以设状态:
表示现在位于点 ,图还差 条边连通,期望还要走多少步。
则最终答案是:
不难得到 之间的转移式:
如果分层高斯消元是 ,考虑设 来递推解决。
带入转移式可得:
于是可以得到两个方程:
这样就可以在 的高斯消元内得到所有的 ,然后递推得到 。
接下来考虑如何计算恰好 条边连通的概率,其实要算的就是 个点 条边连通图的方案数。
这类问题的经典处理方式是用斯特林反演来容斥。
于是考虑划分连通块,不同块之间的边不能选择,同一块内的边可选可不选。
令至少 个连通块的方案数为 ,恰好 个连通块的方案数为 ,两者关系即为:
而我们希望通过计算 得到 ,故 的容斥系数为 。
所以 条边, 个点的连通图数量实际上为:
其中 需要取遍所有的 个点的,划分成了若干连通块的,每个连通块内连成了完全图的图, 表示 的连通块数量, 表示 的边数。
设 表示 个点,划分成了若干连通块,每个连通块内部连成完全图后边数和为 ,的所有图带上容斥系数的 之和,于是考虑 的转移。
首先不难满足 这个系数条件,转移的时候直接乘上 的系数即可。
那么 怎么办呢,注意到这个系数的含义在于,我们希望把同一种划分连通块的方式计算 次,那么不妨赋初值时先确定 号点所在连通块大小,转移的时候枚举下一个连通块的大小为 ,以 的系数重标号,这样除了 号点所在连通块位置一定排在第一个,其他的连通块会因为在转移时对于其添加顺序有区分,有 种排列顺序,满足了我们的需要。
于是有如下转移式:
这样可以 计算出 ,然后 直接算出 ,表示 个点 条边的连通图个数,那么也不难算恰好 条边连通的概率了。
namespace Gauss{
int n, a[N][N], b[N];
inline int get(int x){ return (LL) b[x] * qpow(a[x][x], mod - 2) % mod; }
void init(int m){
n = m;
lfor(i, 1, n) b[i] = 0;
lfor(i, 1, n) lfor(j, 1, n) a[i][j] = 0;
}
void solve(){
lfor(i, 1, n){
int inv = qpow(a[i][i], mod - 2);
lfor(j, 1, n) if(i != j){
int K = (LL) inv * a[j][i] % mod;
lfor(k, 1, n) MOD(a[j][k] -= (LL) K * a[i][k] % mod);
MOD(b[j] -= (LL) K * b[i] % mod);
}
}
}
}
using Gauss :: get;
using Gauss :: init;
using Gauss :: solve;
void get_coef(){
init(n);
lfor(x, 1, n){
Gauss :: b[x] = mod - 1;
Gauss :: a[x][x] = mod - 1;
int v = (LL) q[x] * qpow(s[x], mod - 2) % mod;
lfor(j, 1, s[x]) Gauss :: a[x][a[x][j]] = v;
}
solve();
lfor(x, 1, n) c[x] = get(x);
lfor(z, 1, n){
init(n);
lfor(x, 1, n){
int v = (LL) q[x] * qpow(s[x]) % mod;
if(lnk[x][z]) Gauss :: b[x] = Mod(-(LL) p[x] * qpow(s[x]) % mod);
Gauss :: a[x][x] = mod - 1;
lfor(y, 1, n) if(lnk[x][y]) Gauss :: a[x][y] = v;
}
solve();
lfor(x, 1, n) b[x][z] = get(x);
}
lfor(i, 1, m) lfor(x, 1, n){
lfor(y, 1, n) MOD(e[x][i] += (LL) b[x][y] * e[y][i - 1] % mod - mod);
MOD(e[x][i] += c[x] - mod);
}
}
void get_prob(){
lfor(i, 1, n) dp[i][C(i, 2)] = 1;
lfor(i, 1, n) lfor(j, 0, m) if(dp[i][j]) lfor(k, 1, n - i)
MOD(dp[i + k][j + C(k, 2)] += -(LL) dp[i][j] * C(i + k - 1, k) % mod);
lfor(i, 0, m) lfor(j, i, m)
MOD(g[i] += (LL) dp[n][j] * C(j, i) % mod - mod);
lfor(i, 1, m) g[i] = (LL) g[i] * qpow(C(m, i), mod - 2) % mod;
rfor(i, m, 1) MOD(g[i] -= g[i - 1]);
}
signed main(){
read(n), m = n * (n - 1) / 2;
lfor(i, 1, n) read(p[i]), q[i] = Mod(1 - p[i]);
lfor(i, 1, n){
read(s[i]), a[i] = new int[s[i] + 1];
lfor(j, 1, s[i]) read(a[i][j]), lnk[i][a[i][j]] = 1;
}
prep();
get_coef();
get_prob();
lfor(i, 1, m) MOD(Ans += (LL) e[1][i] * g[i] % mod - mod);
cout << Mod(Ans - 1) << endl;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现