Live2D

CF1067D Computer Game

link

Solution

我们考虑设 \(v=\max_{i} p_ib_i\),可以看出如果一次我们操作成功了那么我们后面就是一直产生 \(m\) 的贡献,那么我们就可以设 \(f_t\) 表示还剩 \(t\) 时间的最大期望贡献,可以得到转移式:

\[f_t=\max_i(p_i((t-1)v+a_i)+(1-p_i)f_{t-1}) \]

\[\Rightarrow f_t=\max_i(p_i((t-1)v-f_{t-1})+p_ia_i)+f_{t-1} \]

那么我们就可以把 \((p_i,a_i)\) 看作是一条 \(y=p_ix+p_ia_i\) 的直线,那么每一次 \(t\) 就相当于取直线上的最大值,这个可以用凸包优化,是一个上凸壳。

一个可以看出的性质是 \((t-1)v-f_{t-1}\) 是单调不降的,我们可以使用反证法,如果 \((t-1)v-f_{t-1}>tv-f_t\rightarrow f_{t}-f_{t-1}>v\),可以看出这显然是矛盾的。

但是 \(t\) 非常的大,所以这样肯定是不行的。但是可以知道的是,因为 \((t-1)v-f_{t-1}\) 是单调不降的,所以一条直线一定是优化一段区间,那么这一段区间转移就可以用矩阵优化,具体来说,如果是 \(p_i,a_i\) 的话,存在转移式:

\[\begin{bmatrix}f_t & t & 1\end{bmatrix}\times \begin{bmatrix}1-p_i & 0 & 0\\vp_i & 1 & 0\\a_ip_i&1&1 \end{bmatrix}=\begin{bmatrix}f_{t+1}&t+1&1\end{bmatrix} \]

那么我们就可以直接倍增处理,时间复杂度 \(\Theta(n\log n)\)

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define int long long
#define MAXN 100005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}

int n,T;
struct Matrix{
	double mat[3][3];
	Matrix(){memset (mat,0,sizeof (mat));}
	double * operator [] (const int &key){return mat[key];}
	Matrix operator * (const Matrix &p)const{
		Matrix New;
		for (Int i = 0;i < 3;++ i)
			for (Int j = 0;j < 3;++ j)
				for (Int k = 0;k < 3;++ k) New[i][k] += mat[i][j] * p.mat[j][k];
		return New;
	}
}f,A[45];

double m,p[MAXN],g[MAXN];
int top,sta[MAXN],ind[MAXN];

#define eps 1e-14

bool cmp (int x,int y){
	if (abs(p[x] - p[y]) < eps) return g[x] < g[y];
	return p[x] < p[y];
}

double getit (double t,int x){
	return p[x] * t + g[x];
}

Matrix getcon (int r){
	Matrix G;
	G[1][1] = G[2][1] = G[2][2] = 1,G[0][0] = 1 - p[r],G[1][0] = m * p[r],G[2][0] = g[r];
	return G;
}

signed main(){
	read (n,T);
	for (Int i = 1;i <= n;++ i){
		double a,b;ind[i] = i;
		scanf ("%lf%lf%lf",&a,&b,&p[i]),g[i] = a * p[i],chkmax (m,b * p[i]);
		// cout << p[i] << " & " << g[i] << endl;
	}
	// cout << "rnm??? " << m << endl;
	sort (ind + 1,ind + n + 1,cmp);
	for (Int i = 1;i <= n;++ i){
		while (top > 1 && (g[ind[i]] - g[sta[top]]) * (p[sta[top]] - p[sta[top - 1]]) >= (g[sta[top]] - g[sta[top - 1]]) * (p[ind[i]] - p[sta[top]])) -- top;
		sta[++ top] = ind[i];
	}
	// cout << "shit??? : ";for (Int i = 1;i <= top;++ i) cout << sta[i] << " , ";cout << endl;
	double px = 0;f[0][2] = 1;
	for (Int now = 0,t = 1;t <= top;){
		while (t < top && getit (px,sta[t]) <= getit (px,sta[t + 1])) ++ t;
		A[0] = getcon (sta[t]);
		for (Int i = 1;i <= 34;++ i) A[i] = A[i - 1] * A[i - 1];
		for (Int i = 34;~i;-- i){
			if (now + (1ll << i) >= T) continue;
			Matrix tmp = f * A[i];
			double New = tmp[0][1] * m - tmp[0][0];
			if (t == top || getit (New,sta[t]) >= getit (New,sta[t + 1]))
				now += (1ll << i),f = tmp,px = New;
		}
		now ++,f = f * A[0],px = m * now - f[0][0];
		if (now == T) break;
	}
	printf ("%.6f\n",f[0][0]);
	return 0;
}
posted @ 2022-10-18 11:24  Dark_Romance  阅读(48)  评论(0编辑  收藏  举报