[CF1067D]Computer Game[凸包/斜率優化+倍增+矩陣乘法]
阿新 • • 發佈:2018-11-29
題意
你有 \(n\) 個任務,初始收益為 \(a\) ,共 \(t\) 輪遊戲,每輪可以選擇完成一個任務(可以做多次),完成之後可以給任意任務升級,升級之後的任務收益為 \(b\) ,每個任務還有完成的概率 \(p\) ,問期望最大收益是多少。
\(n\leq 10^5,1\leq a< b\leq 10^8,t\leq 10^9\)
分析
一個重要而顯然的結論是如果我們有了一次升級的機會,一定升級 \(b*p\) 最大的那一個,之後一直選擇完成這個任務,記 \(M\) 表示最大的 \(b*p\) 。然後只關注沒有一次完成時的最大期望收益。
定義狀態 \(f_t\) 表示還剩 \(t\)
轉移: \(f_{t+1}=\max\{p_i*(tM+a_i)+(1-p_i)*f_t\}\).
可以變形成斜率優化的形式(注意這裡的 \(f_t\) 要看成 \(k\) 的一部分) ,也可以寫成這種形式:
\[f_{t+1}=\max\{p_i*(tM-f_t)+p_ia_i\}+f_t\]
將 \(p_i\) 看成 \(k\) , \(p_ia_i\) 看成 \(b\) ,然後每個轉移都是形如直線 \(y=kx+b\) 的形式。
我們可以維護一個下凸殼,每次可以二分當前的 \(x=tM-f_t\) 在那一段,可以做到 \(Tlogn\)
發現 \(tM-f_t\)是單調不降的,證明如下:
設 \(x_{t+1} \geq x_t\)
有:\(tM-f_t\geq (t-1)M-f_{t-1}\)
\(M\geq f_t-f_{t-1}\)
考慮 \(f\) 的實際意義可以發現上式一定成立,因為一輪的期望收益不會超過 \(M\) 。
然後在每一段倍增矩乘即可。
總時間複雜度為 \(O(n\log T)\)。
程式碼
#include<bits/stdc++.h> using namespace std; #define go(u) for(int i=head[u],v=e[i].to;i;i=e[i].lst,v=e[i].to) #define rep(i,a,b) for(int i=a;i<=b;++i) #define pb push_back typedef long long LL; inline int gi(){ int x=0,f=1;char ch=getchar(); while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();} return x*f; } template<typename T>inline bool Max(T &a,T b){return a<b?a=b,1:0;} template<typename T>inline bool Min(T &a,T b){return b<a?a=b,1:0;} const int N=1e5 + 7; typedef double db; const db eps=1e-12; int n,len,tp,st[N]; LL t; db a[N],b[N],p[N],M; int dcmp(db x){ if(fabs(x)<eps) return 0; return x<0?-1:1; } struct line{ db k,b;int id; line(){}line(db k,db b,int id):k(k),b(b),id(id){} bool operator <(const line &rhs)const{ if(dcmp(k-rhs.k)!=0) return dcmp(k-rhs.k)<0; return dcmp(b-rhs.b)<0; } }l[N]; db X(int a,int b){ return (l[b].b-l[a].b)/(l[a].k-l[b].k); } struct mat{ db v[5][5]; mat(){memset(v,0,sizeof v);} void init(){memset(v,0,sizeof v);} mat operator *(const mat &rhs)const{ mat res; rep(i,0,4) rep(j,0,4) rep(k,0,4) res.v[i][j]+=v[i][k]*rhs.v[k][j]; return res; } }A,B[34],C; mat Pow(mat a,LL b){ mat res; rep(i,1,4) res.v[i][i]=1; for(;b;b>>=1,a=a*a) if(b&1) res=res*a; return res; } int main(){ scanf("%d%I64d\n",&n,&t); rep(i,1,n) { scanf("%lf%lf%lf",&a[i],&b[i],&p[i]); l[i]=line(p[i],p[i]*a[i],i); Max(M,b[i]*p[i]); } sort(l+1,l+1+n); rep(i,1,n) { if(dcmp(l[i].k-l[i+1].k)==0) continue; l[++len]=l[i]; } rep(i,1,len){ while(tp>1&&dcmp(X(st[tp-1],i)-X(st[tp],st[tp-1]))<=0) --tp; st[++tp]=i; } int now=1;LL cnt=0; for(int now=1;now<=tp&&cnt^t;++now){ double R=cnt*M-A.v[1][1]; while(now<tp&&X(st[now],st[now+1])<=R) ++now; int i=st[now];R=X(st[now],st[now+1]); B[0].init(); B[0].v[1][1]=1-l[i].k; B[0].v[2][1]=l[i].k; B[0].v[2][2]=B[0].v[3][1]=B[0].v[3][3]=B[0].v[4][2]=B[0].v[4][4]=1; A.v[1][3]=l[i].b,A.v[1][4]=M; rep(i,1,33) B[i]=B[i-1]*B[i-1]; for(int i=33;~i;--i)if(t-cnt>(1ll<<i)){ C=A*B[i]; if(now==tp||dcmp((cnt+(1ll<<i))*M-C.v[1][1]-R)<0) A=C,cnt+=(1ll<<i); } cnt++,A=A*B[0]; } printf("%.13lf\n",A.v[1][1]); return 0; }