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, ...
随机推荐
- python socket 基本使用
socket通常也叫做"套接字",用于连接server client,是一个通信链的句柄,应用程序通常通过套接字向网络发出请求或应答网络请求. 就像python 处理file一样: ...
- 初始HTML05
HTML 表单控件属性 表单控件可设置以下标签属性 属性名 取值 type 设置控件类型 name 设置控件名称,最终与值一并发送给服务器 value 设置控件的值 placeholder 设置输入框 ...
- ShardingSphere学习
1 基本概念 1.1 ShardingSphere概述 官网:https://shardingsphere.apache.org/index_zh.html 1.2 分库分表概述 分库分表是为了解决由 ...
- Codeforces Round #750 (Div. 2)
Codeforces Round #750 (Div. 2) A. Luntik and Concerts 思路分析: 首先我们可以肯定的是a,b,c都大于等于1,所以我们先让它们自己抵消自己,最后a ...
- 剑指offer:JZ12 矩阵中的路径
JZ12 矩阵中的路径 描述 请设计一个函数,用来判断在一个n乘m的矩阵中是否存在一条包含某长度为len的字符串所有字符的路径.路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上, ...
- Java:反射小记
Java:反射小记 对 Java 中的 反射,做一个微不足道的小小小小记 概念 Java 反射指的是在 Java 程序运行状态中,对于任何一个类,都可以获得这个类的所有属性和方法:对于给定的一个对象, ...
- UltraSoft - Beta - 项目展示
UltraSoft - DDL Killer - Beta 项目展示 团队介绍 CookieLau fmh 王 FUJI LZH DZ(转出) Monster hdl(转入) PM & 后端 ...
- STL模板
目录 栈stack 队列queue 列表List 集合set 映射map 多重映射multimap 对pair 元组tuple 容器containers 算法algorithms 仿函数/函数对象fu ...
- 攻防世界 杂项 4.something_in_image
这是原题 我这里使用编辑器打开,一看乱码也挺多的,于是想了想ctrl+f搜索一下flag关键字吧,结果答案出来了(flag不少,多搜索几次) Flag{yc4pl0fvjs2k1t7T}
- cf Make It Nondeterministic (简单贪心)
有N个人.每个人都有两个名字. 给出这N个人的一个排列.p[1]...p[N]. 现在让每个人挑自己丙个名字中的一个名字.问是否存在一种方案,使得挑出来的N个名字按字典序排完以后正好是p[1]...p ...