题解:
感觉自己是写错了的,好像bzoj的spj有问题。反正这东西没什么用难得改了
拉格朗日乘子法
题解1
讲得已经很好了,大概是这样:
有函数
f(X),g(X)
f
(
X
)
,
g
(
X
)
,其中
X
X
是维向量。
那么
f(X)
f
(
X
)
取最值时的梯度向量与
g(X)
g
(
X
)
的梯度向量平行(证明见上面的连接)
而梯度向量的每一维为 xi x i 的偏导数,那么就有 n+1 n + 1 个方程直接解开就好了。
用二分解方程时间复杂度 O(nlog2n) O ( n log 2 n )
#include <bits/stdc++.h>
#define rdd(x) scanf("%d",&x)
#define rdf(x) cin>>x
#define DB long double
using namespace std;
const int N=1e5+50;
const DB eps=1e-9;
int n;
DB E,s[N],k[N],v[N],ans[N];
inline DB calc(int id,DB x,DB val) {
return 2.0*val*k[id]*x*x*(x-v[id]);
}
inline void solve(int id,DB &x,DB val) {
DB l=((v[id]>0)?v[id]:0),r=1e9;
for(int i=1;i<=70;i++) {
DB mid=(l+r)/2.0;
if(calc(id,mid,val)>-1) l=mid;
else r=mid;
}
x=l; return;
}
inline bool check(DB val) {
for(int i=1;i<=n;i++)
solve(i,ans[i],val);
DB res=0;
for(int i=1;i<=n;i++) res+=s[i]*k[i]*(ans[i]-v[i])*(ans[i]-v[i]);
if(res>E) return false;
return true;
}
int main() {
rdd(n), rdf(E);
for(int i=1;i<=n;i++) rdf(s[i]),rdf(k[i]),rdf(v[i]);
DB l=-1e9,r=0;
for(int i=1;i<=70;i++) {
DB mid=(l+r)/2.0;
if(check(mid)) l=mid;
else r=mid;
}
check(l);
DB res=0;
for(int i=1;i<=n;i++) res+=s[i]/ans[i];
printf("%.8f",(double)res);
}