分数规划及斜率优化

$$ A(\vec{j})+B(\vec{j})F(\vec{i})=G(\vec{i}) $$

$A$ 和 $B$ 是关于 $\vec{j}$ 的函数.

注意,如果确定了 $\vec{j}$ ,那么 $G(\vec{i})$ 便是一条在平面 $(F(\vec{i}),G(\vec{i}))$ 上的直线,即一个以 $\vec{i}$ 为参数的参数方程

$$ \begin{cases}
& y=G(\vec{i})=A_j+B_j F(\vec{i})  \\
& x=F(\vec{i})
\end{cases} $$

 

分数规划

考虑

$$ Minimize \quad \frac{A(\vec{x})}{B(\vec{x})}=\lambda ,\; \vec{x} \in S$$

其中 $\vec{x}$ 叫做"解向量",可以理解为一个参数表 $(x_1,x_2,...,x_n)$ ,S是"可行解集合".

$A(\vec{x})$ 和 $B(\vec{x})$ 是关于 $\vec{x}$ 的连续实值函数.

然后我们定义一个函数 $$ P(\lambda)=\min_{\vec{x}\in S} \left({-B(\vec{x})\lambda+A(\vec{x})}\right) $$

考虑当给定了一个 $\lambda$ 后,如何暴力求 $P(\lambda)$ .

很明显,直接在集合 $S$ 中枚举 $\vec{x}$ ,算出 $B(\vec{x})$ 与 $A(\vec{x})$ ,算出 $-B(\vec{x})\lambda+A(\vec{x})$ ,取最小值即可.

然后我们注意看这个过程. 我们枚举了直线 $$y=-B(\vec{x})\lambda+A(\vec{x})$$

其中自变量 $\lambda$ ,因变量 $y$ .并计算出它的 $y$ 值.

这相当于我们使用一条垂直于水平坐标轴($\lambda$轴)的直线,与所有直线 $y=-B(\vec{x})\lambda+A(\vec{x})$ 取交点,然后取得交点的纵坐标最小的那个.

回到原问题,求 $\lambda$ 的最小值.

我们发现,对于一条直线 $y=-B(\vec{x})\lambda+A(\vec{x})$ ,它的解(使用可行解 $\vec{x}$ 所能给出的 $\lambda$ )正好对应了直线在 $\lambda$ 轴上的截距.

因此,原问题等价于:

$$找出一个\vec{x},使得(由\vec{x}确定的)直线y=-B(\vec{x})\lambda+A(\vec{x})在\lambda轴上的截距最小.$$

或者有时原问题要求的是最小值而不求具体方案,则原问题等价于

$$找出一个\lambda_0 ,使得存在一个可行解\vec{x}\in S,\\ 直线 y=-B(\vec{x})\lambda+A(\vec{x}) 在\lambda轴上的截距为\lambda_0,\\ 且不存在其它可行解\vec{x}\in S,使得直线截距比\lambda_0 小.$$

 

 

 

 这个时候就要用到一些既定的性质了.

 

一般分数规划题目会有$$\forall _{\vec{x}\in S} \; B(\vec{x})>0$$

于是,我们发现直线 $y=-B(\vec{x})\lambda+A(\vec{x})$ 的斜率总是比0小的.

于是 $P(\lambda)$ 是单调的.

那么我们的目标就很明确了:

$$求出函数P(\lambda)与\lambda 轴的左交点.$$

 

红线就是 $\lambda$ 函数图像.可以看到所有直线的$\lambda$轴交点都在右边.

单调就可以二分查找$\lambda$啦.......

但是如何计算$P(\lambda)$呢? 刚才的枚举完全不可做啊.....

呃,这就要具体问题具体分析了.......

一般都会和原算法扯上关系,比如最优比率路径使用最短路径算法计算$P(\lambda)$,最优比率生成树使用最小生成树算法计算$P(\lambda)$,最优比率割使用最小割计算$P(\lambda)$.......

 

AC POJ 2728 最优比率生成树 分数规划+Prim

 

  1 #include <cstdio>
  2 #include <fstream>
  3 #include <iostream>
  4  
  5 #include <cstdlib>
  6 #include <cstring>
  7 #include <algorithm>
  8 #include <cmath>
  9  
 10 #include <queue>
 11 #include <vector>
 12 #include <map>
 13 #include <set>
 14 #include <stack>
 15 #include <list>
 16  
 17 typedef unsigned int uint;
 18 typedef long long int ll;
 19 typedef unsigned long long int ull;
 20 typedef double db;
 21  
 22 using namespace std;
 23  
 24 inline int getint()
 25 {
 26     int res=0;
 27     char c=getchar();
 28     bool mi=false;
 29     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 30     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 31     return mi ? -res : res;
 32 }
 33 inline ll getll()
 34 {
 35     ll res=0;
 36     char c=getchar();
 37     bool mi=false;
 38     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 39     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 40     return mi ? -res : res;
 41 }
 42  
 43 db eps=1e-80;
 44 inline bool feq(db a,db b)
 45 { return fabs(a-b)<eps; }
 46 
 47 template<typename Type>
 48 inline Type avg(const Type a,const Type b)
 49 { return a+((b-a)/2); }
 50 
 51 //===================================================================
 52 //===================================================================
 53 //===================================================================
 54 //===================================================================
 55 
 56 const int INF=(1<<30)-1;
 57 
 58 int n;
 59 
 60 db M[1050][1050]; //adjust table.
 61 db dist[1050][1050];
 62 
 63 int px[1050];
 64 int py[1050];
 65 int ph[1050];
 66 
 67 db Dist(db ax,db ay,db bx,db by)
 68 { return sqrt((ax-bx)*(ax-bx)+(ay-by)*(ay-by)); }
 69 
 70 bool intree[1050];
 71 db dst[1050];
 72 
 73 db MST(db f)
 74 {
 75     for(int i=0;i<n;i++)
 76     for(int j=i;j<n;j++)
 77     M[i][j]=M[j][i]=fabs(ph[i]-ph[j])-f*dist[i][j];
 78     
 79     for(int i=0;i<n;i++)
 80     dst[i]=1e40,intree[i]=false;
 81     
 82     db res=0.0;
 83     
 84     intree[0]=true;
 85     for(int i=0;i<n;i++)
 86     dst[i]=min(dst[i],M[0][i]);
 87     
 88     for(int d=1;d<n;d++)
 89     {
 90         db mi=1e40;
 91         int mp=-1;
 92         for(int i=0;i<n;i++)
 93         if(!intree[i] && mi>dst[i])
 94         {
 95             mi=dst[i];
 96             mp=i;
 97         }
 98         
 99         intree[mp]=true;
100         res+=dst[mp];
101         
102         for(int i=0;i<n;i++)
103         if(!intree[i])
104         dst[i]=min(dst[i],M[mp][i]);
105     }
106     
107     return res;
108 }
109 
110 int main()
111 {
112     eps=1e-4;
113     
114     while(true)
115     {
116         n=getint();
117         if(n==0) break;
118         
119         for(int i=0;i<n;i++)
120         for(int j=0;j<n;j++)
121         M[i][j]=INF;
122         
123         for(int i=0;i<n;i++)
124         {
125             px[i]=getint()-1;
126             py[i]=getint()-1;
127             ph[i]=getint();
128         }
129         
130         for(int i=0;i<n;i++)
131         for(int j=i;j<n;j++)
132         dist[i][j]=dist[j][i]=Dist(px[i],py[i],px[j],py[j]);
133         
134         db l=0,r=1e7;
135         while(fabs(r-l)>eps)
136         {
137             db mid=(l+r)*0.5;
138             if(MST(mid)>0.0) l=mid;
139             else r=mid;
140         }
141         
142         printf("%.3f\n",l);
143     }
144     
145     return 0;
146 }
View Code

 

具体用分数规划解决问题的时候就直接

1.确定比式 $\frac{A(\vec{x})}{B(\vec{x})}=\lambda$

2.构造函数 $P(\lambda)=min(A(\vec{x})+B(\vec{x})\lambda )$.

  这道题是 $P(\lambda)=min(\vec{a}\cdot\vec{x}-\vec{b}\cdot\vec{x} \lambda)$ ,

  其中 $\vec{a}$ 代表高度差数组(答案的分子部分), $\vec{b}$ 代表距离数组(答案的分母部分).

3.整理表达式.这道题是 $min((\vec{a}-\lambda \vec{b})\cdot\vec{x})$

4.考虑用某种常规方法拿到 $P(\lambda)$. 这道题由 $min((\vec{a}-\lambda \vec{b})\cdot\vec{x})$ 的意义,知这个值可以用MST算法求出.

5.二分/三分 $\lambda$ 达到精度要求后直接输出.

 

 

 

 

 

斜率优化

一般用在DP中,求$$\min_{0\le j < i} A(j)+B(j)\cdot F(i) $$

也可以求 $max$ ,这里只讨论 $min$ .

注意到每一个j确定了一条直线 $ y=B(j)x+A(j) $

当我们用暴力枚举 $j$ 计算状态 $i$ 的转移的时候,实际上我们在枚举直线 $y=B(j)x+A(j)$ ,然后把 $x=F(i)$ 代入,求得y的最小值..

根据上边某个图可以知道其实就是求一个下凸壳(或上凸壳)与直线 $x=F(i)$ 的交点.

这样我们维护凸壳就行了$=\omega=$(说得倒简单!)

呃嗯,斜率优化是这样一种东西..

我们需要找一些性质......

我们就可以比较方便地解决问题.....

 

1.斜率有单调性.

此时所有的直线的斜率 $B(j)$ 按照 $j$ 的顺序单调递减(或递增,这里只讨论递减). 画一下图就知道,我们需要维护上凸壳(下半平面交),并且由于斜率递减的原因,我们总是从后往前(从右往左)剔除一些没用的直线.

具体的,考虑新加入一条直线 $l_0$ ,而最右边的直线,按照斜率记为 $l_1$ , $l_2$ ,($l_1$ 斜率小于 $l_2$ 斜率,即 $l_1$ 是半平面交的直线栈中最后一个元素 ),然后判断 $l_0$ 与 $l_2$ 的交点是不是在 $l_1$ 的正下方.

如果是,那么 $l_1$ 一定不会被选中成为最优的转移状态,因为 $l_1$ 与 $l_2$ 已经完全把它的正下方遮住了.

重复这个剔除直线的操作直到不能剔除( $l_0$ 与 $l_2$ 交点在 $l_1$ 上方 )为止.

 

这样我们就可以直接像GRAHAM一样维护了......

配合二分(三分)查找(就是二分(三分)找到一条直线 $l$ ,处在它左边的(即凸壳栈中和它左相邻的)直线 $l^{-}$ 和 $x=F(i)$ 的交点在 $l$ 上方,处在它右边的直线 $l^{+}$ 和 $x=F(i)$ 的交点也在 $l$ 上方. ),可以得到它的值. 复杂度 $O(nlogn)$

 

2.决策参数 $F(i)$ 有单调性

这个东西需要斜率单调.

斜率单调以后,如果 $F(i)$ 随着 $i$ 表现单调的话,我们可以从凸壳某一侧排除一些不再能够成为解的直线.

 

如果凸壳最左边的直线 $l_0$ 比右边的直线 $l_1$ 的解更差,那么我们可以断定,对于以后的 $x=F(i)$ ,这条直线都不会成为解. 于是可以搞一个单调队列,相当于指针扫描,队列在不断插入新边的同时也不断地剔除左边没用的直线.可以在 $O(n)$ 时间解决问题.

 

如果两个条件都不满足,也能做,因为只要是类似于 $min$ 或 $max$ 函数中的表达式是直线形式,都可以形成凸壳,这样我们需要splay/线段树维护凸壳,二分查找.

有些神题还要可持久化.......

 

AC BZOJ 1010

  1 #include <cstdio>
  2 #include <fstream>
  3 #include <iostream>
  4  
  5 #include <cstdlib>
  6 #include <cstring>
  7 #include <algorithm>
  8 #include <cmath>
  9  
 10 #include <queue>
 11 #include <vector>
 12 #include <map>
 13 #include <set>
 14 #include <stack>
 15 #include <list>
 16  
 17 typedef unsigned int uint;
 18 typedef long long int ll;
 19 typedef unsigned long long int ull;
 20 typedef double db;
 21  
 22 using namespace std;
 23  
 24 inline int getint()
 25 {
 26     int res=0;
 27     char c=getchar();
 28     bool mi=false;
 29     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 30     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 31     return mi ? -res : res;
 32 }
 33 inline ll getll()
 34 {
 35     ll res=0;
 36     char c=getchar();
 37     bool mi=false;
 38     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 39     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 40     return mi ? -res : res;
 41 }
 42  
 43 db eps=1e-20;
 44 inline bool feq(db a,db b)
 45 { return fabs(a-b)<eps; }
 46 
 47 template<typename Type>
 48 inline Type avg(const Type a,const Type b)
 49 { return a+((b-a)/2); }
 50 
 51 //==========================================================
 52 //==========================================================
 53 //==========================================================
 54 //==========================================================
 55 
 56 ll INF=((ll)1<<56)-1;
 57 
 58 ll n,L;
 59 
 60 ll d[50050];
 61 ll c[50050];
 62 ll s[50050];
 63 
 64 ll bd[50050];
 65 
 66 ll F[50050];
 67 ll A[50050];
 68 ll B[50050];
 69 ll K[50050];
 70 
 71 ll C;
 72 
 73 /*
 74 inline ll F(int i)
 75 { return i+s[i]; }
 76 
 77 inline ll A(int i)
 78 { return F(i)*F(i)-2*C*F(i); }
 79 
 80 inline ll B(int i)
 81 { return F(i)*F(i)+2*C*F(i)+d[i]; }
 82 
 83 inline ll K(int i)
 84 { return -2*F(i); }
 85 */
 86 
 87 int stk[50050],hp=0,tp=0;
 88 inline int getans(int i)
 89 {
 90     while( hp+1<tp && B[stk[hp]]-B[stk[hp+1]] >= (K[stk[hp+1]]-K[stk[hp]])*F[i] )
 91     hp++;
 92     return stk[hp];
 93 }
 94 
 95 inline void addline(ll b,ll k,int i)
 96 {
 97     while(  hp+1<tp &&
 98             (b-B[stk[tp-1]])*(k-K[stk[tp-2]]) >=
 99             (b-B[stk[tp-2]])*(k-K[stk[tp-1]]) )
100         tp--;
101     stk[tp++]=i;
102 }
103 
104 
105 
106 int main()
107 {
108     n=getint();
109     L=getint();
110     C=L+1;
111     
112     for(int i=1;i<=n;i++) c[i]=getint();
113     for(int i=1;i<=n;i++) s[i]=s[i-1]+c[i];
114     
115     tp=1;
116     F[0]=0+s[0];
117     A[0]=(F[0]-(C<<1))*F[0];
118     K[0]=-(F[0]<<1);
119     B[0]=(F[0]+(C<<1))*F[0]+d[0];
120         
121     for(int i=1;i<=n;i++)
122     {
123         F[i]=i+s[i];
124         A[i]=(F[i]-(C<<1))*F[i];
125         K[i]=-(F[i]<<1);
126         
127         int j=getans(i);
128         d[i]=B[j]+K[j]*F[i];
129         d[i]+=A[i]+C*C;
130         
131         B[i]=(F[i]+(C<<1))*F[i]+d[i];
132         addline(B[i],K[i],i);
133     }
134     
135     printf("%lld\n",d[n]);
136     
137     return 0;
138 }
View Code

 

裸的斜率优化.

1.注意队列的端界. 我们允许队列中只存在一个元素(即,凸壳中有且仅有一条直线). 即便当我们加入了另一条直线,使凸壳绝不可能只含一条直线,我们也需要那样判断以免出现坑爹情况,尤其是使用单调队列的时候.

2.公式千万别推错啊!.....

3.注意下标之间的关系......栈中存的是状态编号.........

 

AC BZOJ 3156

 

  1 #include <cstdio>
  2 #include <fstream>
  3 #include <iostream>
  4  
  5 #include <cstdlib>
  6 #include <cstring>
  7 #include <algorithm>
  8 #include <cmath>
  9  
 10 #include <queue>
 11 #include <vector>
 12 #include <map>
 13 #include <set>
 14 #include <stack>
 15 #include <list>
 16  
 17 typedef unsigned int uint;
 18 typedef long long int ll;
 19 typedef unsigned long long int ull;
 20 typedef double db;
 21  
 22 using namespace std;
 23  
 24 inline int getint()
 25 {
 26     int res=0;
 27     char c=getchar();
 28     bool mi=false;
 29     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 30     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 31     return mi ? -res : res;
 32 }
 33 inline ll getll()
 34 {
 35     ll res=0;
 36     char c=getchar();
 37     bool mi=false;
 38     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 39     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 40     return mi ? -res : res;
 41 }
 42  
 43 db eps=1e-80;
 44 inline bool feq(db a,db b)
 45 { return fabs(a-b)<eps; }
 46 
 47 template<typename Type>
 48 inline Type avg(const Type a,const Type b)
 49 { return a+((b-a)/2); }
 50 
 51 //===================================================================
 52 //===================================================================
 53 //===================================================================
 54 //===================================================================
 55 
 56 int n;
 57 ll a[1005000];
 58 ll d[1005000];
 59 
 60 ll A[1005000];
 61 ll K[1005000];
 62 
 63 int stk[1005000],hp,tp;
 64 int getans(ll v)
 65 {
 66     while( hp+1<tp &&
 67             A[stk[hp]]-A[stk[hp+1]] >= (K[stk[hp+1]]-K[stk[hp]])*v )
 68         hp++;
 69     return stk[hp];
 70 }
 71 void pushline(ll a,ll k,int i)
 72 {
 73     while( hp+1<tp &&
 74             (a-A[stk[tp-1]])*(k-K[stk[tp-2]]) >= (a-A[stk[tp-2]])*(k-K[stk[tp-1]]) )
 75         tp--;
 76     stk[tp++]=i;
 77 }
 78 
 79 
 80 int main()
 81 {
 82     n=getint();
 83     for(int i=1;i<=n;i++) a[i]=getint();
 84     
 85     hp=0; tp=1;
 86     
 87     for(int i=1;i<=n;i++)
 88     {
 89         int j=getans(i);
 90         d[i]=A[j]+K[j]*i;
 91         d[i]+=a[i]+(ll)(i-1)*i/2;
 92         
 93         A[i]=d[i]+(ll)(i+1)*i/2;
 94         K[i]=-i;
 95         
 96         pushline(A[i],K[i],i);
 97     }
 98 
 99     printf("%lld\n",d[n]);
100     return 0;
101 }
View Code

 

由于循环变量用的int,结果乘起来爆精度了.......WA了好几发.......

为什么都这时候了还错在这种sb问题上........

 而且推式子的时候居然傻逼地把min提一个1/2常数出来结果把常数项从min里拿到外面又忘记乘.........妈呀.......

 

 

AC BZOJ 3675 APIO2014 T2

比较裸的斜率优化.

 

  1 #include <cstdio>
  2 #include <fstream>
  3 #include <iostream>
  4  
  5 #include <cstdlib>
  6 #include <cstring>
  7 #include <algorithm>
  8 #include <cmath>
  9  
 10 #include <queue>
 11 #include <vector>
 12 #include <map>
 13 #include <set>
 14 #include <stack>
 15 #include <list>
 16  
 17 typedef unsigned int uint;
 18 typedef long long int ll;
 19 typedef unsigned long long int ull;
 20 typedef double db;
 21  
 22 using namespace std;
 23  
 24 inline int getint()
 25 {
 26     int res=0;
 27     char c=getchar();
 28     bool mi=false;
 29     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 30     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 31     return mi ? -res : res;
 32 }
 33 inline ll getll()
 34 {
 35     ll res=0;
 36     char c=getchar();
 37     bool mi=false;
 38     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
 39     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
 40     return mi ? -res : res;
 41 }
 42  
 43 db eps=1e-20;
 44 inline bool feq(db a,db b)
 45 { return fabs(a-b)<eps; }
 46 
 47 template<typename Type>
 48 inline Type avg(const Type a,const Type b)
 49 { return a+((b-a)/2); }
 50 
 51 //==============================================================================
 52 //==============================================================================
 53 //==============================================================================
 54 //==============================================================================
 55 
 56 const ll INF=((ll)1<<56)-1;
 57 
 58 int n;
 59 int m;
 60 
 61 int a[105000];
 62 ll s[105000];
 63 ll s2[105000];
 64 ll d[2][105000];
 65 
 66 ll b[2][105000];
 67 
 68 int st[105000];
 69 int h,t;
 70 void INIT(){ h=t=0; }
 71 
 72 inline void Insert(int l,ll B,ll K,int id)
 73 {
 74     while(h<t-1 && (B-b[l][st[t-1]])*(K-s[st[t-2]]) >=
 75                     (B-b[l][st[t-2]])*(K-s[st[t-1]]) ) t--;
 76     st[t++]=id;
 77 }
 78 
 79 inline int Get(int l,ll x)
 80 {
 81     while( h<t-1 && b[l][st[h+1]]-b[l][st[h]] >=
 82                     (s[st[h]]-s[st[h+1]])*x ) h++;
 83     return st[h];
 84 }
 85 
 86 int main()
 87 {
 88     n=getint();
 89     m=getint();
 90     for(int i=1;i<=n;i++)
 91     s[i]=s[i-1]+(a[i]=getint()),s2[i]=s[i]*s[i];
 92     
 93     int cur=0;
 94     
 95     for(int i=0;i<=n;i++)
 96     b[1][i]=-s2[i];
 97     
 98     memset(d[!cur],0,sizeof(ll)*(n+1));
 99     
100     for(int r=1;r<=m;r++)
101     {    
102         memset(d[cur],0,sizeof(ll)*(n+1));
103         INIT();
104         Insert(!cur,b[!cur][0],s[0],0);
105         
106         for(int i=1;i<=n;i++)
107         {
108             int v=Get(!cur,s[i]);
109             d[cur][i]=d[!cur][v]-s2[v]+s[v]*s[i];
110             
111             b[cur][i]=d[cur][i]-s2[i];
112             
113             Insert(!cur,b[!cur][i],s[i],i);
114         }
115         
116         cur=!cur;
117     }
118     printf("%lld\n",d[!cur][n]);
119     
120     return 0;
121 }
View Code

 

唯一的不同在于多了一维....

状态是从d[cur-1]转移至d[cur],所以单调队列里的所有东西都是d[cur-1]里的内容.

然后为了节省空间.....我这里写了滚动数组.......

注意倒式子的时候的符号...... "不断论证推导的每一步是否正确,为什么正确."

注意这道题要用到一个性质:一旦确定了所有切割点,那么切割顺序不影响最终结果. 如何证明?

 

 AC BZOJ1911

 1 #include <cstdio>
 2 #include <fstream>
 3 #include <iostream>
 4  
 5 #include <cstdlib>
 6 #include <cstring>
 7 #include <algorithm>
 8 #include <cmath>
 9  
10 #include <queue>
11 #include <vector>
12 #include <map>
13 #include <set>
14 #include <stack>
15 #include <list>
16  
17 typedef unsigned int uint;
18 typedef long long int ll;
19 typedef unsigned long long int ull;
20 typedef double db;
21  
22 using namespace std;
23  
24 inline int getint()
25 {
26     int res=0;
27     char c=getchar();
28     bool mi=false;
29     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
30     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
31     return mi ? -res : res;
32 }
33 inline ll getll()
34 {
35     ll res=0;
36     char c=getchar();
37     bool mi=false;
38     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
39     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
40     return mi ? -res : res;
41 }
42 
43 //==============================================================================
44 //==============================================================================
45 //==============================================================================
46 //==============================================================================
47 
48 int n;
49 ll A,B,C;
50 
51 ll a[1005000];
52 ll s[1005000];
53 ll d[1005000];
54 
55 ll stb[1005000];
56 ll stk[1005000];
57 int sh,st;
58 void Insert(ll b,ll k)
59 {
60     while(st>1 && (stk[st-2]-k)*(stb[st-2]-stb[st-1])<(stk[st-2]-stk[st-1])*(stb[st-2]-b)) st--;
61     stb[st]=b;
62     stk[st]=k;
63     st++;
64 }
65 
66 ll Get(ll v)
67 {
68     while(sh<st-1 && stb[sh]+stk[sh]*v < stb[sh+1]+stk[sh+1]*v) sh++;
69     return stb[sh]+stk[sh]*v;
70 }
71 
72 int main()
73 {
74     n=getint();
75     A=getint();
76     B=getint();
77     C=getint();
78     
79     for(int i=1;i<=n;i++)
80     a[i]=getint();
81     
82     for(int i=1;i<=n;i++)
83     s[i]=s[i-1]+a[i];
84     
85     Insert(0,0);
86     for(int i=1;i<=n;i++)
87     {
88         d[i]=Get(s[i])+A*s[i]*s[i]+B*s[i]+C;
89         Insert(A*s[i]*s[i]-B*s[i]+d[i],-2*A*s[i]);
90     }
91     
92     printf("%lld\n",d[n]);
93     
94     return 0;
95 }
View Code

 

推式子整个推错了居然没发现QAQ

注意正负号,用直线式算出来那个坐标值一般都带负号的.

急着A题结果把减号打成乘号居然也没有发现啊QAQ

打题的时候一定要冷静....冷静........

 

AC BZOJ1096 裸的斜率优化

 

 1 #include <cstdio>
 2 #include <fstream>
 3 #include <iostream>
 4  
 5 #include <cstdlib>
 6 #include <cstring>
 7 #include <algorithm>
 8 #include <cmath>
 9  
10 #include <queue>
11 #include <vector>
12 #include <map>
13 #include <set>
14 #include <stack>
15 #include <list>
16  
17 typedef unsigned int uint;
18 typedef long long int ll;
19 typedef unsigned long long int ull;
20 typedef double db;
21 typedef long double ldb;
22  
23 using namespace std;
24  
25 inline int getint()
26 {
27     int res=0;
28     char c=getchar();
29     bool mi=false;
30     while((c<'0' || c>'9')/* && !feof(stdin)*/) mi=(c=='-'),c=getchar();
31     while('0'<=c && c<='9'/* && !feof(stdin)*/) res=res*10+c-'0',c=getchar();
32     return mi ? -res : res;
33 }
34 inline ll getll()
35 {
36     ll res=0;
37     char c=getchar();
38     bool mi=false;
39     while(c<'0' || c>'9') mi=(c=='-'),c=getchar();
40     while('0'<=c && c<='9') res=res*10+c-'0',c=getchar();
41     return mi ? -res : res;
42 }
43 
44 //==============================================================================
45 //==============================================================================
46 //==============================================================================
47 //==============================================================================
48 
49 
50 
51 struct factory
52 { ll x,p,c; }F[1005000];
53 bool fcmp(const factory&a,const factory&b)
54 { return a.x<b.x; }
55 ll s[1005000];
56 ll t[1005000];
57 int n;
58 
59 ll B[1005000];
60 ll K[1005000];
61 int qh,qt;
62 ll Get(ll x)
63 {
64     while(qh<qt-1 && B[qh]-B[qh+1] > x*(K[qh+1]-K[qh]) ) qh++;
65     return B[qh]+K[qh]*x;
66 }
67 ll Insert(ll b,ll k)
68 {
69     while( qt>qh+1 && 
70           (K[qt-2]-k)*(B[qt-2]-B[qt-1])<=(K[qt-2]-K[qt-1])*(B[qt-2]-b) ) qt--;
71     B[qt]=b; K[qt]=k; qt++; 
72 }
73 
74 ll d[1005000];
75 
76 int main()
77 {
78     n=getint();
79     for(int i=1;i<=n;i++)
80     F[i].x=getint(),F[i].p=getint(),F[i].c=getint();
81     sort(F,F+n,fcmp);
82     for(int i=1;i<=n;i++) s[i]=s[i-1]+F[i].p;
83     for(int i=1;i<=n;i++) t[i]=t[i-1]+F[i].x*F[i].p;
84     
85     Insert(0,0);
86     for(int i=1;i<=n;i++)
87     {
88         ll v=Get(F[i].x);
89         d[i]=v+F[i].x*s[i]-t[i]+F[i].c;
90         Insert(d[i]+t[i],-s[i]);
91     }
92     
93     printf("%lld\n",d[n]);
94     
95     return 0;
96 }
View Code

 

又被 long long 坑掉 TAT....

 

随手在结构体里打了int结果就WA了一发.....

 

 

 另一种斜率优化

考虑方程:$$ \max_{0\le j<i}{ x_j A_i + Y_j B_i }$$

此时我们多了一个与i相关的控制变量,所以用于评判结果的直线不再是垂直于x轴的线.

考虑平面上的点 $(X_j,Y_j)$ . 对于一个确定的 $A_i$ 以及 $B_i$ , 我们可以构造一条直线 $ p=x A_i + y B_i $ .

这样,如果直线经过了一个点 $(X_j,Y_j)$ ,这条直线的 $p$ 值就等于我们所要求的 $ X_j A_i + Y_j B_i $ .

于是,我们可以想象一下,一条直线 $p=A_i x + N_i y$ ,一开始p为一个非常大的数,然后p一直减少,这样直线就会一直向下平移,直到我们碰到一个点 $(X_j,Y_j)$ ,那么我们就取得了最大的那个 $p$ ,就是我们要求的结果.

但是如何计算呢? 我们可以任选一个p的值,搞一条直线出来. 考虑点到直线的距离

$$ D = \frac{| A_i X_j + B_i Y_j + p |}{\sqrt{A_i^2 + B_i^2} } $$

如果把绝对值去掉,就可以根据符号判断点在直线的上方还是下方.

进一步,由于 $\sqrt{A_i^2+B_i^2}$ 不影响点到直线距离的相对大小和符号(我们把所有点到直线的距离同时乘上这个数),得到一个可行判据

$$ D^\prime = A_i X_j + B_i Y_j + p $$

画个图就知道,如果我们按照凸壳上的点从左到右的顺序算这个 $D^\prime$ 值,会发现它先增大,后减小,是一个上凸函数.

三分法找到D值最大的那个点就可以直接算出答案了.

值得注意的是,本分析中使用的是Max函数,维护上凸壳. 而上边另一个方程使用Min函数,维护的也是上凸壳.

两个模型是否能相互转化呢?

 

 

 

 

 


 原来cnblogs的LaTeX不能使用CopyPaste...

 

 


 

从大仙那里讨教来了一些神奇的东西......

china-pub

同方数据库

柳高也买有数据库....

还有《组合数学》 by richard a.brualdi

 

posted @ 2015-03-23 21:37  DragoonKiller  阅读(505)  评论(0编辑  收藏  举报