Codeforces 题面传送门 & 洛谷题面传送门

好题。

首先显然我们如果在某一次游戏中升级,那么在接下来的游戏中我们一定会一直打 \(b_jp_j\) 最大的游戏 \(j\),因为这样得到的期望收益最大。

因此我们设 \(dp_i\) 表示还剩 \(i\) 秒并且当前没有升级过的最大收益。

那么有 \(dp_i=\max\limits_{j}\{dp_{i-1}(1-p_j)+X(i-1)p_j+p_ja_j\}\),其中 \(X=\max\{b_jp_j\}\)。

稍微解释一下上面的转移方程,我们枚举这一次选择玩哪个游戏,设为 \(j\),那么我们有 \(p_j\) 的概率获得胜利,之后每一轮期望会获得 \(X\) 的收益,得到的总收益就是 \(X(i-1)\),此外该轮还会获得 \(a_j\) 的收益,这种情况下的期望收益就是 \(X(i-1)p_j+p_ja_j\)。如果这次游戏没有取得胜利,那么问题转化为还剩 \(i-1\) 秒的情况,最大收益为 \(dp_{i-1}\),概率为 \(1-p_j\),期望收益为 \((1-p_j)dp_{i-1}\),把两者加起来可以得到 \(dp_{i-1}(1-p_j)+X(i-1)p_j+p_ja_j\)。对所有 \(j\) 取个 \(\max\) 即可得到上面的式子。

我们设 \(S_i=X(i-1)-dp_{i-1}\),那么上面的转移方程可以写成 \(dp_i=\max\{dp_{i-1}+p_jS_i+p_ja_j\}\)。注意到 \(S_j\) 是单调不减的,因为 \(S_{j+1}-S_{j}=(iX-dp_i)-(X(i-1)-dp_{i-1})=X-(dp_i-dp_{i-1})\),而 \(dp_i-dp_{i-1}\) 这个式子我们调用它的实际意义可知,它们的差距不可能大于一轮中的最大收益 \(X\),因此 \(X-(dp_i-dp_{i-1})\ge 0\)。

注意到上面改写过的 \(dp\) 方程可以视作,我们有若干个点 \((p_j,a_jp_j)\),你要对于所有点,过其做斜率为 \(-S_i\),取最大截距作为 \(dp_i\) 相较于 \(dp_{i-1}\) 的增量,看到这样的设问我们可以很自然地想到斜率优化。具体来说我们建出这 \(n\) 个点的上凸壳,那么最优切点肯定在上凸壳上,又因为 \(S_i\) 单调递增,因此最优切点的横坐标肯定不断向右移,因此我们可以均摊 \(\mathcal O(n+T)\) 地对于每一轮 DP 求出其最优切点。

如果这题 \(T\) 的数据范围比较小那么按照上文所述进行斜率优化即可通过,不过这丧心病狂的出题人偏偏将 \(T\) 数据范围加强到 \(10^{10}\)。这就导致直接一轮轮推过去的做法无法通过,不过注意到对于上凸壳上每个点,它作为最优转移点存在的时刻是一段区间,因此我们考虑从左到右遍历上凸壳上每一个点,二分它作为最优转移点的区间的右端点 \(t\),矩阵快速幂算出 \(t\) 时刻的 DP 值,并通过判断 \(S_t\) 的最优切点是否是该点来判断应当向左还是向右二分。具体来说,对于一段连续的且最优转移点均为 \(j\) 的 DP,它们的 DP 转移均可写成以下形式:

\[\begin{bmatrix}dp_i&i&1\end{bmatrix}\times
\begin{bmatrix}
1-p_j&0&0\\
p_jX&1&0\\
p_ja_j&1&1
\end{bmatrix}=
\begin{bmatrix}dp_{i+1}&i+1&1\end{bmatrix}
\]

矩阵快速幂即可。这样复杂度 \(n\log^2n\),再加上矩阵快速幂的大常数,有亿点点危,不过如果我们对于所有 \(k\in[0,\log_2(t)]\) 预处理出矩阵的 \(2^k\) 幂,然后倍增最优转移点区间的右端点,那么时间复杂度可以做到 \(n\log n\)。

const int MAXN=1e5;
const int LOG_T=36;
int sgn(ld x){return (x<-EPS)?-1:((x<EPS)?0:1);}
struct mat{
ld a[4][4];
mat(){for(int i=1;i<=3;i++) for(int j=1;j<=3;j++) a[i][j]=0;}
mat operator *(const mat &rhs){
mat res;
for(int i=1;i<=3;i++) for(int j=1;j<=3;j++)
for(int k=1;k<=3;k++) res.a[i][k]+=a[i][j]*rhs.a[j][k];
return res;
}
} cur,pw[LOG_T+2];
int n,a[MAXN+5],b[MAXN+5];
ll t;ld p[MAXN+5],X=0;
struct point{
ld x,y;int id;
point(ld _x=0,ld _y=0,int _id=0):x(_x),y(_y),id(_id){}
bool operator <(const point &rhs) const{
if(sgn(x-rhs.x)) return sgn(x-rhs.x)<0;
return sgn(y-rhs.y)<0;
}
} P[MAXN+5];
int stk[MAXN+5],tp=0;
void calc_hull(){
sort(P+1,P+n+1);
for(int i=1,j;i<=n;i=j+1){
j=i;while(!sgn(P[i].x-P[j].x)) j++;j--;
while(tp>1&&(P[stk[tp]].y-P[stk[tp-1]].y)*(P[j].x-P[stk[tp]].x)<
(P[stk[tp]].x-P[stk[tp-1]].x)*(P[j].y-P[stk[tp]].y)) --tp;
stk[++tp]=j;
}
}
ld calc(point p,ld x){return p.x*x+p.y;}
int main(){
scanf("%d%lld",&n,&t);
for(int i=1;i<=n;i++){
scanf("%d%d%Lf",&a[i],&b[i],&p[i]);
P[i]=point(p[i],a[i]*p[i],i);chkmax(X,b[i]*p[i]);
}
calc_hull();
ll curp=0;cur.a[1][3]=1;
// for(int i=1;i<=tp;i++) printf("(%.10Lf %.10Lf)\n",P[stk[i]].x,P[stk[i]].y);
for(int i=1;;){
ld curS=X*curp-cur.a[1][1];
// printf("%lld %.10Lf\n",curp,curS);
while(i<tp&&(P[stk[i+1]].y-P[stk[i]].y)>=-curS*(P[stk[i+1]].x-P[stk[i]].x)) i++;
int id=P[stk[i]].id;
pw[0].a[1][1]=1-p[id];pw[0].a[1][2]=0;pw[0].a[1][3]=0;
pw[0].a[2][1]=p[id]*X;pw[0].a[2][2]=1;pw[0].a[2][3]=0;
pw[0].a[3][1]=p[id]*a[id];pw[0].a[3][2]=1;pw[0].a[3][3]=1;
for(int j=1;j<=LOG_T;j++) pw[j]=pw[j-1]*pw[j-1];
for(int j=LOG_T;~j;j--) if(curp+(1ll<<j)<t){
mat nw_mat=cur*pw[j];
ld nw=X*nw_mat.a[1][2]-nw_mat.a[1][1];
if((i==tp)||calc(P[stk[i]],nw)>calc(P[stk[i+1]],nw))
curp+=(1ll<<j),cur=cur*pw[j];
} cur=cur*pw[0];curp++;if(curp==t) break;
} printf("%.10Lf\n",cur.a[1][1]);
return 0;
}

Codeforces 1067D - Computer Game(矩阵快速幂+斜率优化)的更多相关文章

  1. Codeforces 691E题解 DP+矩阵快速幂

    题面 传送门:http://codeforces.com/problemset/problem/691/E E. Xor-sequences time limit per test3 seconds ...

  2. CodeForces - 691E Xor-sequences 【矩阵快速幂】

    题目链接 http://codeforces.com/problemset/problem/691/E 题意 给出一个长度为n的序列,从其中选择k个数 组成长度为k的序列,因为(k 有可能 > ...

  3. Codeforces 691E Xor-sequences(矩阵快速幂)

    You are given n integers a1,  a2,  ...,  an. A sequence of integers x1,  x2,  ...,  xk is called a & ...

  4. Codeforces 954 dijsktra 离散化矩阵快速幂DP 前缀和二分check

    A B C D 给你一个联通图 给定S,T 要求你加一条边使得ST的最短距离不会减少 问你有多少种方法 因为N<=1000 所以N^2枚举边数 迪杰斯特拉两次 求出Sdis 和 Tdis 如果d ...

  5. POJ 3150 Cellular Automaton --矩阵快速幂及优化

    题意:给一个环,环上有n块,每块有个值,每一次操作是对每个点,他的值变为原来与他距离不超过d的位置的和,问k(10^7)次操作后每块的值. 解法:一看就要化为矩阵来做,矩阵很好建立,大白书P157页有 ...

  6. hdu 1575 Tr A(矩阵快速幂乘法优化算法)

    Problem Description A为一个方阵,则Tr A表示A的迹(就是主对角线上各项的和),现要求Tr(A^k)%. Input 数据的第一行是一个T,表示有T组数据. 每组数据的第一行有n ...

  7. hihocoder第41周 骨牌覆盖(矩阵快速幂)

    由于棋盘只有两行,所以如果第i列的骨牌竖着放,那么就转移为第1列到第i-1列骨牌有多少种摆法 如果第一行第i列骨牌横着放,那么第二行第i列也要横着放,那么就转移为了第1列到第i-2列骨牌有多少种方法 ...

  8. 【BZOJ1009】GT考试(KMP算法,矩阵快速幂,动态规划)

    [BZOJ1009]GT考试(KMP算法,矩阵快速幂,动态规划) 题面 BZOJ 题解 看到这个题目 化简一下题意 长度为\(n\)的,由\(0-9\)组成的字符串中 不含串\(s\)的串的数量有几个 ...

  9. 2019.02.11 bzoj4818: [Sdoi2017]序列计数(矩阵快速幂优化dp)

    传送门 题意简述:问有多少长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数,且其中至少有一个数是质数,答案对201704082017040820170408取模(n≤1e9, ...

随机推荐

  1. 【java】关键字this怎么用?

    摘至:https://www.php.cn/java/guide/462643.html java中this关键字的用法:1.当成员变量和局部变量重名时,在方法中使用this时,表示的是该方法所在类中 ...

  2. .net Xml加密解密操作

    生成密钥的方法: /// <summary>生成RSA加密 解密的 密钥 /// 生成的key就是 方法EncryptByRSA与DecryptByRSA用的key了 /// </s ...

  3. Java:HashMap类小记

    Java:HashMap类小记 对 Java 中的 HashMap类,做一个微不足道的小小小小记 概述 HashMap:存储数据采用的哈希表结构,元素的存取顺序不能保证一致.由于要保证键的唯一.不重复 ...

  4. [Beta]the Agiles Scrum Meeting 3

    会议时间:2020.5.14 20:00 1.每个人的工作 今天已完成的工作 成员 已完成的工作 yjy 实现前端界面美化 tq 实现查看.删除测试点功能的前端修复功能中的bug wjx 升级系统实现 ...

  5. CF375D Tree and Queries 题解

    感觉CF的题目名都好朴素的样子 你谷链接 首先这题显然是个dsu on tree 但是我不会. 其次这题显然是个莫队.这我会啊! 然后会发现好像不是很对劲.因为每次询问都有一个k,貌似和传统的莫队数颜 ...

  6. STM32的I2C框图详解及通讯过程

    STM32 的I2C 特性及架构 如果我们直接控制STM32 的两个GPIO 引脚,分别用作SCL 及SDA,按照上述信号的时序要求,直接像控制LED 灯那样控制引脚的输出(若是接收数据时则读取SDA ...

  7. Spring Cache 带你飞(一)

    Spring 3.1 版本引入基于 annotation 的 cache 技术,提供了一套抽象的缓存实现方案,通过注解方式使用缓存,基于配置的方式灵活使用不同缓存组件.代码具有相当的灵活性和扩展性,本 ...

  8. Spring Boot 2.5.0 重新设计的spring.sql.init 配置有何用?

    前几天Spring Boot 2.5.0发布了,其中提到了关于Datasource初始化机制的调整,有读者私信想了解这方面做了什么调整.那么今天就要详细说说这个重新设计的配置内容,并结合实际情况说说我 ...

  9. hdu 1158 Employment Planning(DP)

    题意: 有一个工程需要N个月才能完成.(n<=12) 给出雇佣一个工人的费用.每个工人每个月的工资.解雇一个工人的费用. 然后给出N个月所需的最少工人人数. 问完成这个项目最少需要花多少钱. 思 ...

  10. 第11课 OpenGL 飘动的旗帜

    飘动的旗帜: 这一课从第六课的代码开始,创建一个飘动的旗帜.我相信在这课结束的时候,你可以掌握纹理映射和混合操作. 大家好!对那些想知道我在这里作了些什么的朋友,您可以先按文章的末尾所列出的链接,下载 ...