[BZOJ1597]土地の購入
フェイス質問
ファーマージョン彼の農場を拡大する準備ができて、彼は幅広い各プロット(1 <=を満たすためにN(1 <= N <= 50,000)長方形の土地。長さと幅を検討しています<
= 1,000,000; ... 1 <=長さ<= 1,000,000)それがある土地の面積あたりのコストが、FJを購入することができ、高速ながら、これらの土地の地価
グリッドは、彼らの最大の幅を乗じた彼らの最大の長さですが、土地の長さと幅は、交換はできません。FJは、土地の一部とは3x5の5×3の土地を購入した場合、彼が必要
フーの5x5 = 25 FJは、すべての土地を購入したいが、彼はお金を節約するために土地を購入するためにこれらのグループを発見した。彼はあなたが彼が最小の資金を見つける必要があります。
入力
* 1行目:数:N
*最初の行2..N + 1:I + 1番目のラインは、二つの数字、それぞれi番目のブロックランドの長さおよび幅が含ま
出力
*最初の行:可能な限り最小の費用。
サンプル入力
4
100 1
15 15
20 5。
1つの100
入力解釈:
4つの土地の合計。
サンプル出力
500
FJは、3つのグループに分け、これらの土地を買う:
第1グループ:、100x1
第二群1x100、
。20x5及び15×15のプロットの第三の組の
各グループの価格は100100300、500の合計でした。
考え
土地は、土地A、Bが含まれている場合は明らかに、我々は直接関係の土地Aをあきらめることができます。包含関係を決定する方法を考えてみましょう。
長さを降順でソートされたすべての土地の初。そして、新しい配列に土地を回します。なお、現在のアレイにロードされている土地の幅は土地の最後の部分に等しい未満であれば、あなたはありません(付属)配列に、直流電流の土地を放棄することができます。
そして、我々はそれが単調に長さを短くしていることを発見してもらう新しい配列を見て、幅が単調に増加しています。その後、我々は容易に状態遷移方程式に発生する可能性があります。
\ [F [I] =分(F [J] +長さ[J + 1] *幅[I]),. 1 \のLeq J <I \]
直接暴力移動錯体度は\(O(N ^ 2) \) 揚げすることは確かです。勾配の最適化を使用することを検討して:
\ [順序X =幅[i]は、K =長さ[J + 1]、B = F [J] \\ F [J] +長さ[J + 1] *幅を[I] = KX + B \]
質問は、次にように直線に変換\(X = F [I] \) 、その最小座標点直線との全ての交点を見つけます。
要約すると、最適化の斜面には、いくつかの条件を満たしている必要があります。
- 単調kは
//継続されます
コード
各ことに注意while(...){head++;}
最適解は、の頭の後に発見されない\(J \) 。しかし、セグメントが数字に対応します。線分を破棄することができるので、そのように対応するセグメントは、に対応するセグメントの数以下である(J \)\します。
範囲を確保すべきである場合に加えて、状態遷移方程式を書く\を(<iは\)の代わりに(\ \のLeq I \) 。我々は再挿入最初のクエリなので、クエリは対象外であるときので(私は\)\の。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define maxn 55000
#define ll long long
struct line{
ll k,b;
}l[maxn];
struct field{
int wid,len;
}fie1[maxn],fie2[maxn];
int n,head=1,tail,cntn;
ll f[maxn];
bool cmp(field x,field y){
if(x.len==y.len){return x.wid>y.wid;}
return x.len>y.len;
}
bool calc(line l1,line l2,int c){return (double)(l1.b-l2.b)/(l2.k-l1.k)<=c;}
bool calc(line l1,line l2,line now){return
(double)(l1.b-l2.b)/(l2.k-l1.k)>=(double)(l1.b-now.b)/(now.k-l1.k);}
int main(){
//freopen("in","r",stdin);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&fie1[i].wid,&fie1[i].len);
sort(fie1+1,fie1+1+n,cmp);
for(int i=1;i<=n;i++){
if(fie1[i].wid>fie2[cntn].wid){fie2[++cntn]=fie1[i];}
}
n=cntn;l[++tail]=(line){fie2[1].len,0};//l[1].k=fie2[1].len;
for(int i=1;i<=n;i++){
while(head<tail&&calc(l[head],l[head+1],fie2[i].wid))head++;
f[i]=l[head].k*fie2[i].wid+l[head].b;
line now=(line){fie2[i+1].len,f[i]};
while(head<tail&&calc(l[tail],l[tail-1],now))tail--;
l[++tail]=now;
}
printf("%lld",f[n]);
return 0;
}