P2179-[NOI2012]騎行川藏【導數,二分】
阿新 • • 發佈:2021-07-08
正題
題目連結:https://www.luogu.com.cn/problem/P2179
題目大意
給出\(E\)和\(n\)個\(s_i,k_i,u_i\)求一個序列\(v_i\)滿足
\[\sum_{i=1}^nk_is_i(v_i-u_i)^2\leq E \]的情況下最小化
\[\sum_{i=1}^n\frac{s_i}{v_i} \]\(1\leq n\leq 10^4\)
解題思路
洛谷題解上一個十分神奇的做法看起來。(主要是看不懂拉格朗日乘數法/kk)
首先考慮對於段路的行駛時間\(t_i=\frac{s_i}{v_i}\),我們可以畫出消耗的能量\(E\)和\(t_i\)的函式。
對於函式\(f(E)=t_i\)不難發現的是在\(v_i\geq u_i\)的情況下\(E\)越小這個函式對應位置的導數越小。
也就是消耗單位能量減少的時間也就越少,價效比就越低。而我們現在要給每段路分配一個\(t_i\)使得消耗能量和等於\(E\)且\(t_i\)和最小的話。
根據貪心的思想有選出若干個的\(t_i\)滿足對應位置的導數相等。
那麼我們就找到了所有路的共性,考慮二分這個導數,但是我們先對這個函式\(f(v)=\frac{t}{E}\)求個導。
\[t'=-\frac{s}{v_i^2},E'=2k_is_i(v_i-u_i) \]\[f'(v)=\frac{t'}{E'}=-\frac{s}{2k_is_iv_i^2(v_i-u_i)} \]然後我們二分出\(f'(v_i)=x\)
code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=1e4+10; int n;double E,s[N],k[N],v[N]; double getv(double x,int p){ double l=max(v[p],0.0),r=100000; for(int i=1;i<=100;i++){ double V=(l+r)/2.0; if(-2.0*k[p]*V*V*x*(V-v[p])<1.0)l=V; else r=V; } return (l+r)/2.0; } double check(double x){ double E=0; for(int i=1;i<=n;i++){ double V=getv(x,i); E+=k[i]*s[i]*(V-v[i])*(V-v[i]); } return E; } int main() { scanf("%d",&n);scanf("%lf",&E); for(int i=1;i<=n;i++) scanf("%lf%lf%lf",&s[i],&k[i],&v[i]); double l=-1e5,r=0; for(int i=1;i<=100;i++){ double mid=(l+r)/2.0; if(check(mid)<=E)l=mid; else r=mid; } double mid=(l+r)/2.0,ans=0; for(int i=1;i<=n;i++) ans+=s[i]/getv(mid,i); printf("%.12lf\n",ans); return 0; }