AtCoder Beginner Contest 199 F - Graph Smoothing(图的邻接矩阵幂的意义,数学期望,矩阵快速幂)
Posted 繁凡さん
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AtCoder Beginner Contest 199 F - Graph Smoothing(图的邻接矩阵幂的意义,数学期望,矩阵快速幂)相关的知识,希望对你有一定的参考价值。
整理的算法模板合集: ACM模板
实际上是一个全新的精炼模板整合计划
Weblink
https://atcoder.jp/contests/abc199/tasks/abc199_f
Problem
Solution
我们要知道图的邻接矩阵的幂的意义,详见 P3758 [TJOI2017]可乐
解题报告(九) 矩阵与行列式(ACM / OI)超高质量题解
这个图就非常形象:
读题之后发现显然就是建图然后求 k k k 次幂即可,因为有 k k k 次互不影响的操作嘛。
我们拥有的操作是等概率地选择一条边,将这条边连接的两个点的点权变成两个点的点权的平均值。显然很难维护每一条边连接的两个点的平均值,所以我们换个思想,仅需维护每一个点得到的贡献即可。
显然对于每一个点的期望权值,只有自己对自己的贡献(概率),以及自己所连向的点对自己的贡献。
我们设 d i d_i di 为点 i i i 的度数。
首先考虑自己对自己的贡献:
那么对于一个点来说,不做任何操作的话对自己的贡献即为自己的点权 a i a_i ai,但是当我选择一条边的时候,每一个点一共连有 d i d_i di 条边,所以每一个点被选中的概率显然为 d i m \\cfrac{d_i}{m} mdi,那么一个点被选中之后,它自己的贡献就从 a i a_i ai 变成了 a i 2 \\cfrac{a_i}{2} 2ai(因为我们进行的是取平均数的操作),所以原来的期望为 1 × a i 1\\times a_i 1×ai ,每次有 d i m \\cfrac{d_i}{m} mdi 的概率选到点 i i i,被选择一次期望权值就减少 a i 2 \\cfrac{a_i}{2} 2ai,即 贡献为 1 − d i 2 × m = 2 × m − d i 2 × m 1-\\cfrac{d_i}{2\\times m}=\\cfrac{2\\times m-d_i}{2\\times m} 1−2×mdi=2×m2×m−di。自己( i i i)对自己( i i i)的贡献就是邻接矩阵里的 a [ i ] [ i ] a[i][i] a[i][i]。
然后考虑其他与之相连的点对自己的贡献:
那么对于任意一条边 ⟨ u , v ⟩ ⟨u,v⟩ ⟨u,v⟩,考虑点 u u u 对点 v v v 的贡献,显然选择到该边的概率为 1 m \\cfrac{1}{m} m1, u u u 给 v v v 的贡献就是平均数 1 2 \\cfrac{1}{2} 21。别人( j j j)给自己( i i i)的贡献就是邻接矩阵里的 a [ j ] [ i ] a[j][i] a[j][i]。
我们计算出每一个点得到的贡献(概率)之后,自己对自己的贡献乘上自己本身的点权,别人对自己的贡献(概率)乘上别人的点权就是每个点在 k k k 次操作后的点权的期望。
Time
O
(
n
3
l
o
g
k
)
O(n^3logk)
O(n3logk)
Code
// Problem: F - Graph Smoothing
// Contest: AtCoder - AtCoder Beginner Contest 199(Sponsored by Panasonic)
// URL: https://atcoder.jp/contests/abc199/tasks/abc199_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
using namespace std;
#define int long long
typedef long long ll;
const int N = 507, mod = 1e9 + 7;
int res[N][N];
int a[N][N];
int val[N];
int e[N][N];
int n, m, k;
int d[N];
void mul(int c[], int a[], int b[][N])
{
int tmp[N] = {0};
for(int j = 1; j <= n; ++ j)
for(int k = 1; k <= n; ++ k)
tmp[j] = (tmp[j] + a[k] * b[k][j]) % mod;
memcpy(c, tmp, sizeof tmp);
}
void mul(int c[][N], int a[][N], int b[][N])
{
int tmp[N][N] = {0};
for(int i = 1; i <= n; ++ i)
for(int j = 1; j <= n; ++ j)
for(int k = 1; k <= n; ++ k)
tmp[i][j] = (tmp[i][j] + a[i][k] * b[k][j]) % mod;
memcpy(c, tmp, sizeof tmp);
}
int qpow(int a, int b)
{
int res = 1;
while(b) {
if(b & 1) res = 1ll * res * a % mod;
a = 1ll * a * a % mod;
b >>= 1;
}
return res;
}
signed main()
{
scanf("%lld%lld%lld", &n, &m, &k);
for(int i = 1; i <= n; ++ i)
scanf("%lld", &val[i]);
for(int i = 1; i <= m; ++ i) {
int x, y;
scanf("%lld%lld", &x, &y);
e[x][y] = e[y][x] = 1;
d[x] ++ ;
d[y] ++ ;
}
int inv_2m = qpow(2 * m, mod - 2);
for(int i = 1; i <= n; ++ i) {
for(int j = 1; j <= n; ++ j) {
if(e[i][j]) {
a[j][i] = inv_2m;//别人给自己的贡献(j 给 i 的贡献)
}
}//自己给自己的贡献(i 给 i 的贡献)
a[i][i] = (2 * m - d[i]) * inv_2m % mod;
}
for(int i = 1; i <= n; ++ i)
res[i][i] = 1;
while(k) {
if(k & 1) mul(res, res, a);
mul(a, a, a);
k >>= 1;
}
for(int i = 1; i <= n; ++ i) {
ll ans = 0;
for(int j = 1; j <= n; ++ j)
ans = (ans + (1ll * res[j][i] * val[j]) % mod) % mod;
cout << ans << endl;
}
return 0;
}
以上是关于AtCoder Beginner Contest 199 F - Graph Smoothing(图的邻接矩阵幂的意义,数学期望,矩阵快速幂)的主要内容,如果未能解决你的问题,请参考以下文章
AtCoder Beginner Contest 115 题解