三对于斜率优化,分为三种类型,一是斜率单调,第二种是是斜率不单调可以二分,第三种便是毫无规律可言的斜率情况,采用李超线段树可以在log范围内快速查询与修改,且能够胜任全部情况,代码与时间复杂度都很优秀。
一,将每个dp[i]的状态看做一条直线,提取转化成具有固定的截距,斜率的式子
二,查询操作来寻找当前决策最优的函数值
方法为在值域内带入当前xi,取函数值的最优
f为计算函数值函数 i是离散化之后的x坐标,c[i]为该离散化坐标,t为直线编号,x[t]为其固定斜率,y[t]为其固定截距
- double f(int i,int t)
- {
- return y[t]+x[t]*c[i];
- }
李超线段树k维护的是当前x轴区域线段编号,l,r为离散化之后x坐标的范围,s[k]是当前x的最优决策线段
每次我们查询当前决策最优的情况时,带入u=当前x,逐一定位u的精确范围,然后回溯答案。
- double qry(int k,int l,int r)
- {
-
- if(l==r)
- return f(u,s[k]);
- int m=(l+r)>>1;
-
- return max(f(u,s[k]),u>m?qry(k<<1|1,m+1,r):qry(k<<1,l,m));
-
- }
三,插入直线操作
插入直线操作就是将原本最优决策点替换的操作
每次我们取一段线段的中间值,如果新直线在中点处函数值大于旧直线,那么就交换决策点。
然后朝着使新直线更优的一侧进行递归,直到到达一个点时,再次比对函数值即可
- void upd(int k,int t,int l,int r)
- {
- if(l==r)
- {
- if(f(l,t)>f(l,s[k]))
- s[k]=t;
- return ;
- }
- int m=(l+r)>>1;
- if(f(m,t)>f(m,s[k]))
- swap(t,s[k]);
-
- f(l,t)>f(l,s[k])?upd(k<<1,t,l,m):upd(k<<1|1,t,m+1,r);
- }
- # include
- using namespace std;
-
- typedef long long int ll;
-
- double a[100000+10],b[100000+10],c[100000+10],d[100000+10],k[100000+10],x[100000+10],y[100000+10],r[100000+10];
- int n,u;
- double f;
- int mp[100000*4+10];
-
- # define getval( i, id) (c[i]*x[id]+y[id])
-
- double query(int root,int l,int r)
- {
- if(l==r)
- {
- return getval(u,mp[root]);
- }
-
- int mid=(l+r)>>1;
-
- return max(getval(u,mp[root]),u>mid?query(root<<1|1,mid+1,r):query(root<<1,l,mid));
-
- }
-
- void update(int root,int t,int l,int r)
- {
- if(l==r)
- {
- if(getval(l,t)>getval(l,mp[root]))
- mp[root]=t;
- return ;
- }
- int mid=(l+r)>>1;
-
- if(getval(mid,t)>getval(mid,mp[root]))
- swap(mp[root],t);
-
- getval(l,t)>getval(l,mp[root])?update(root<<1,t,l,mid):update(root<<1|1,t,mid+1,r);
- }
- int main()
- {
-
- cin>>n>>f;
-
- for(int i=1;i<=n;i++)
- {
-
- scanf("%lf%lf%lf",&a[i],&b[i],&r[i]);
-
- c[i]=a[i]/b[i];
- d[i]=c[i];
- }
-
- sort(c+1,c+1+n);
-
- for(int i=1;i<=n;i++)
- {
- u=lower_bound(c+1,c+1+n,d[i])-c;
-
- f=max(f,b[i]*query(1,1,n));
-
- x[i]=f/(a[i]+b[i]/r[i]);
- y[i]=x[i]/r[i];
-
- update(1,i,1,n);
-
- }
-
- cout<
setprecision(3)< - return 0;
- }