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} 12×mdi=2×m2×mdi。自己( 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 234

AtCoder Beginner Contest 115 题解

AtCoder Beginner Contest 154 题解

AtCoder Beginner Contest 103

AtCoder Beginner Contest 228

AtCoder Beginner Contest 242