「解题报告」CF1067D Computer Game

发布时间 2023-07-19 12:25:41作者: APJifengc

快国赛了,写点水题玩吧。

首先容易有一个贪心策略:先以某种最优策略一直进行,直到成功一次后一直选择 \(b_i p_i\) 最大的进行。我们可以列出一个 DP,设 \(f_T\) 表示在 \(T\) 时刻内期望最大收益,容易写出:

\[f_T = \max \{p_i ((T - 1) v + a_i) + (1-p_i) f_{T - 1}\} \]

看起来就是可以斜率优化的,整理可得:

\[f_T = f_{T - 1} + \max \{p_i ((T - 1) v - f_{T - 1}) + p_i a_i\} \]

显然是可以斜率优化的,那么我们先把凸包建出来,然后考虑怎么做。

首先我们证明 \(k = Tv - f_T\) 是单调的。不难发现每次 \(k\) 会增加 \(v - (f_T - f_{T - 1})\),而由于 \(a_i < b_i\),那么说明一步之内能获得的最大收益就是 \(v\),那么这个增加的数就一定是一个正数,于是说明这个 \(k\) 一定是单调递增的。

而根据斜率优化的结论,我们得知转移过程的所有最优决策点一定是划分成若干段,分别对应凸包上的一个点。上述的式子容易写成矩阵的形式,那么我们就可以直接矩阵快速幂二分到每个段的分割点即可。使用倍增就是单 \(\log\) 了。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const double EPS = 1e-10;
int n;
long long T;
int a[MAXN], b[MAXN];
long double p[MAXN];
struct Matrix {
	long double a[3][3];
	Matrix() { memset(a, 0, sizeof a); }
	static Matrix diag() { Matrix a; a[0][0] = a[1][1] = a[2][2] = 1; return a; }
	long double* operator[](int b) { return a[b]; }
	const long double* operator[](const int b) const { return a[b]; }
	Matrix operator*(Matrix b) const {
		Matrix c;
		for (int k = 0; k < 3; k++)
			for (int i = 0; i < 3; i++)
				for (int j = 0; j < 3; j++)
					c[i][j] += a[i][k] * b[k][j];
		return c;
	}
};
long double slope(int i, int j) {
	if (p[i] == p[j]) return a[i] < a[j] ? INFINITY : -INFINITY;
	return (a[j] * p[j] - a[i] * p[i]) / (p[j] - p[i]);
}
int stk[MAXN], top;
pair<long double, int> pt[MAXN];
long double v;
Matrix get(int i) {
	Matrix m;
	m[0][0] = 1 - p[i];
	m[1][0] = p[i] * v, m[1][1] = 1;
	m[2][0] = p[i] * a[i], m[2][1] = 1, m[2][2] = 1;
	return m;
}
Matrix w[66];
long double value(Matrix m, int i) {
	return p[i] * (m[2][1] * v - m[2][0]) + p[i] * a[i];
}
int main() {
	scanf("%d%lld", &n, &T);
	for (int i = 1; i <= n; i++) {
		scanf("%d%d%Lf", &a[i], &b[i], &p[i]);
		pt[i] = { p[i], i };
		v = max(v, b[i] * p[i]);
	}
	sort(pt + 1, pt + 1 + n);
	for (int i = 1; i <= n; i++) {
		while (top > 1 && slope(stk[top], pt[i].second) - slope(stk[top - 1], stk[top]) > -EPS) top--;
		stk[++top] = pt[i].second;
	}
	Matrix q = Matrix::diag();
	for (int i = 1; i <= top; i++) if (T && (i == top || value(q, stk[i]) - value(q, stk[i + 1]) > -EPS)) {
		w[0] = get(stk[i]);
		for (int j = 1; j <= 60; j++) {
			w[j] = w[j - 1] * w[j - 1];
		}
		for (int j = 60; j >= 0; j--) if (T > (1ll << j)) {
			auto tmp = q * w[j];
			long double f = tmp[2][0];
			if (i == top || value(tmp, stk[i]) - value(tmp, stk[i + 1]) > -EPS) {
				q = tmp;
				T -= (1ll << j);
			}
		}
		long double f = q[2][0];
		q = q * w[0];
		T--;
	}
	printf("%.12Lf\n", q[2][0]);
	return 0;
}