Codeforces 1067D - Computer Game(矩阵快速幂+斜率优化)
好题。
首先显然我们如果在某一次游戏中升级,那么在接下来的游戏中我们一定会一直打 \(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}
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(矩阵快速幂+斜率优化)的更多相关文章
- Codeforces 691E题解 DP+矩阵快速幂
题面 传送门:http://codeforces.com/problemset/problem/691/E E. Xor-sequences time limit per test3 seconds ...
- CodeForces - 691E Xor-sequences 【矩阵快速幂】
题目链接 http://codeforces.com/problemset/problem/691/E 题意 给出一个长度为n的序列,从其中选择k个数 组成长度为k的序列,因为(k 有可能 > ...
- Codeforces 691E Xor-sequences(矩阵快速幂)
You are given n integers a1, a2, ..., an. A sequence of integers x1, x2, ..., xk is called a & ...
- Codeforces 954 dijsktra 离散化矩阵快速幂DP 前缀和二分check
A B C D 给你一个联通图 给定S,T 要求你加一条边使得ST的最短距离不会减少 问你有多少种方法 因为N<=1000 所以N^2枚举边数 迪杰斯特拉两次 求出Sdis 和 Tdis 如果d ...
- POJ 3150 Cellular Automaton --矩阵快速幂及优化
题意:给一个环,环上有n块,每块有个值,每一次操作是对每个点,他的值变为原来与他距离不超过d的位置的和,问k(10^7)次操作后每块的值. 解法:一看就要化为矩阵来做,矩阵很好建立,大白书P157页有 ...
- hdu 1575 Tr A(矩阵快速幂乘法优化算法)
Problem Description A为一个方阵,则Tr A表示A的迹(就是主对角线上各项的和),现要求Tr(A^k)%. Input 数据的第一行是一个T,表示有T组数据. 每组数据的第一行有n ...
- hihocoder第41周 骨牌覆盖(矩阵快速幂)
由于棋盘只有两行,所以如果第i列的骨牌竖着放,那么就转移为第1列到第i-1列骨牌有多少种摆法 如果第一行第i列骨牌横着放,那么第二行第i列也要横着放,那么就转移为了第1列到第i-2列骨牌有多少种方法 ...
- 【BZOJ1009】GT考试(KMP算法,矩阵快速幂,动态规划)
[BZOJ1009]GT考试(KMP算法,矩阵快速幂,动态规划) 题面 BZOJ 题解 看到这个题目 化简一下题意 长度为\(n\)的,由\(0-9\)组成的字符串中 不含串\(s\)的串的数量有几个 ...
- 2019.02.11 bzoj4818: [Sdoi2017]序列计数(矩阵快速幂优化dp)
传送门 题意简述:问有多少长度为n的序列,序列中的数都是不超过m的正整数,而且这n个数的和是p的倍数,且其中至少有一个数是质数,答案对201704082017040820170408取模(n≤1e9, ...
随机推荐
- javascript-jquery对象的动画处理
一.显示与隐藏动画效果 1.hide(动画持续时间,easing用来指定切换效果,动画执行完毕调用函数): $("p").hide(5000,"swing",f ...
- Django Model字段加密的优雅实现
早前的一篇文章Django开发密码管理表实例有写我们写了个密码管理工具来实现对密码的管理,当时加密解密的功能在view层实现,一直运行稳定所以也没有过多关注实现是否优雅的问题.最近要多加几个密码表再次 ...
- 2021北航敏捷软工Beta阶段评分与总结
概述 Beta 阶段评分,按照之前的规则,主要组成部分为: 博客部分,基于 Beta 阶段博客的评分(每篇正规博客 10 分,每篇 Scrum5 分,评定方式类比往年) 评审部分,基于 Beta 阶段 ...
- [软软软]技术博客-Commitizen优化git commit
工具介绍 commitizen/cz-cli是一个规范git commit的工具,使用它代替git commit能够方便有效地写好提交的log,使得团队项目的版本信息更清晰. 安装 (全局安装) np ...
- CSP-S2021 退役记
首先大家一起恭喜博主以5pts之差与省三擦肩而过!(nmd爷去年都省三今年成功打铁了) 果然这个菜鸡一年不如一年了 upd:T3死在多测上了,随便一个40+28的人可以吊打我 Day -2: 模拟赛, ...
- 2021.8.9考试总结[NOIP模拟34]
T1 Merchant 如果$t=0$时不能达到$s$,那么所拿物品的价值一定关于时间单调递增,答案单调.因此可以特判$0$后二分. 用$sort$复杂度被卡,要用$\textit{nth_eleme ...
- 转:Linux常用命令总结
学习linux也有一阵子了,现总结一些常用的linux操作命令,方便大家查找1. cd命令这个命令是最基本的也是最常用的.它用于切换当前目录,可以是绝对路径,也可以是相对路径.例:cd /root/h ...
- v3
#include <iostream> #include <time.h> #include "map" #include "stdio.h&qu ...
- pycharm安装指导教程
pycharm下载安装教程 1.pycharm下载官网 http://www.jetbrains.com/pycharm/ 2.下载好安装包后双击点开安装包文件,这边以专业版为例 按以上图片所示教程, ...
- Linux&C open creat read write lseek 函数用法总结
一:五个函数的参数以及返回值. 函数 参数 返回值 open (文件名,打开方式以及读 ...