Editorial for Danger at the Marks


Approach

The input provides a matrix AA. We can see that Ai,jkA^k_{i,j} gives us the probability of ending at position jj after kk steps, when starting from position ii.

For a given start ss:

E[Collisions]=∑tPr⁡[Collision at time t]E[\text{Collisions}] = \sum_t \Pr[\text{Collision at time } t]

(by linearity of expectation)

=∑t∑i=1nAs,itA1,it= \sum_t \sum_{i=1}^{n} A^t_{s,i} A^t_{1,i}.

A collision at time tt occurs precisely when Eric and Lucas occupy the same mark. They move independently, so the probability they both land at mark ii is As,itA1,itA^t_{s,i} A^t_{1,i}, and summing over ii gives the probability of a collision. The same sum is the (s,1)(s, 1) entry of At(At)⊤A^t (A^t)^\top, since

(At(At)⊤)s,1=∑i=1nAs,it((At)⊤)i,1=∑i=1nAs,itA1,it(A^t (A^t)^\top)_{s,1} = \sum_{i=1}^{n} A^t_{s,i} ((A^t)^\top)_{i,1} = \sum_{i=1}^{n} A^t_{s,i} A^t_{1,i}.

The expectation is then ∑t(At(At)⊤)s,1\sum_t (A^t (A^t)^\top)_{s,1}. Matrix addition is entrywise, so this is the (s,1)(s, 1) entry of ∑tAt(At)⊤\sum_t A^t (A^t)^\top. (It turns out, entry (x,y)(x, y) gives us the EV for Lucas starting at xx, and Eric starting at yy.)

Naively calculating this takes O(n3k)O(n^3 k), which is not fast enough. We can find a recurrence to speed this up.

Define f(k)f(k) to equal the sum from 11 to kk:

f(k)=∑t=1kAt(At)⊤f(k) = \sum_{t=1}^{k} A^t (A^t)^\top.

f(0)f(0) is the zero matrix.

f(k)=Af(k−1)A⊤+AA⊤f(k) = A f(k - 1) A^\top + A A^\top when kk is odd.

f(k)=Ak/2f(k/2)(A⊤)k/2+f(k/2)f(k) = A^{k/2} f(k/2) (A^\top)^{k/2} + f(k/2) when kk is even.

Using this recurrence, and being careful to calculate all required powers of AA with log⁡k\log k matrix multiplications, we can evaluate ff in O(n3log⁡k)O(n^3 \log k).

Solution (C++)

Code 1
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for(int i = a; i < (b); ++i)
#define all(x) begin(x), end(x)
#define sz(x) (int)(x).size()
#define pb push_back
#define nl '\n'
#define fr first
#define sc second
typedef long long ll;
typedef pair<int, int> pii; typedef vector<int> vi;

const ll mod = 1e9 + 7;

ll modpow(ll a, ll p) {
	ll res = 1;
	while(p) {
		if (p&1)res=res*a%mod;
		a=a*a%mod;
		p/=2;
	}
	return res;
}

const ll inv100 = modpow(100, mod-2);
typedef vector<array<ll,100>> mat;
int n;

mat matmul(const mat &a, const mat &b) {
	mat res(n);
	rep(i,0,n) rep(j,0,n) rep(k,0,n) {
		(res[i][j] += a[i][k] * b[k][j]) %= mod;
	}
	return res;
}

mat matadd(const mat &a, const mat &b) {
	mat res(n);
	rep(i,0,n) rep(j,0,n) res[i][j] = (a[i][j] + b[i][j])%mod;
	return res;
}

// :3
mat trans(const mat &a) {
	mat res(n);
	rep(i,0,n) rep(j,0,n) {
		res[i][j] = a[j][i];
	}
	return res;
}

mat id;

int main() {
	cin.tie(0)->sync_with_stdio(0);

	ll k;
	cin >> n >> k;

	id = mat(n);
	rep(i,0,n) id[i][i] = 1;

	mat graph(n);

	rep(i,0,n) rep(j,0,n) {
		cin >> graph[i][j];
		(graph[i][j] *= inv100) %= mod;
	}


	mat grapht = trans(graph);

	// graph^k, sum
	auto recurse = [&](auto && self, ll k) -> array<mat,2> {
		if (k == 0) return {id, mat(n)};
		if (k % 2 == 0) {
			auto [ha,sum] = self(self, k/2);
			auto a = matmul(ha,ha);
			auto nw = matmul(matmul(ha, sum), trans(ha));
			nw = matadd(nw, sum);

			return {a,nw};
		} else {
			auto [ha,sum] = self(self,k-1);
			auto a = matmul(ha,graph);
			auto nw = matmul(matmul(graph,sum),grapht);
			nw = matadd(nw, matmul(graph,grapht));
			return {a,nw};
		}
	};

	auto [a,res] = recurse(recurse,k);
	rep(i,1,n) cout << res[i][0] << " \n"[i == n - 1];
}

Comments0


No comments yet

Be the first to comment.

New comment


Log in to join the discussion.