一般的な問題の意味:あなたの長さの2つを与えるために、\(N- \)配列\(a_iを\)と\(B_i \) 、の定義\(F_K(X)= \ sum_ {i = 1} ^ K | a_ix + b_i | \) 、のための\(K = 1 \ SIM N \) 各\(F_K \) 、シーク\(F_K \)最低。
事前知識
まず、簡単な質問を考えてみます。
\(問題\ 1 \) :
あなたの長さを与えるために\(N- \)アレイ\(B_i \) 、シーク(\ \ sum_ {i = 1 | | X + b_i \} ^ n)の最小値。
中学校の数学明確にするとき、理解\(X \)テイク\(B_i \)の中央値は逆の番号は、この式が最小値をとります。
質問のアップグレード版を考えてみます。
\(問題\ 2 \) :
あなたの長さの2つの得\(N- \)アレイ\(a_iを\)と\(B_iを\)を探している、\(\ sum_ {i = 1 | | a_ix + b_i \} ^ n)の最小値を。(と仮定すると任意の\(a_iを> 0 \) )
これは、見つけるのは難しいことではない、実際には、複雑に見えるかもしれません(\ | a_ix + b_i | = a_iを| | X- + \ {a_iをFRAC b_i} {})\、である\(| a_ix + b_i | \ ) に分割することができます\ (a_iを\)\(| X + \ {a_iをFRAC B_i} {} | \) 。
そして、上記押してください\(問題\ 1 \)メソッドを行うことができます。
なお、我々は仮定上に\(a_iを> 0 \)が、実際にはのために\(a_iを<0 \)の場合、我々は同時に缶\(a_iを\)と\(b_i \)私たちができること、その逆の数となります会う\(a_iを> 0 \) 。
さらに、対象は存在しない特別な命令を有していない\(a_iを= 0 \)場合の、しかし、データは、このような状況が存在していないん私は、いくつかの特別な治療を受ける可能性があるこのような状況が発生した練習しますしかし、私は、書き込みするのが面倒です。
前処理
私たちは、この質問は、我々は、実際には、各時間は一連の追加やるべきことがわかります(a_iを\)\と(b_i \)\、メンテナンス\(問題\ 2 \)答えを。
我々は場合は、\(| a_ix + b_i | \ ) に分割され(a_iを\)\ (\ | | X + \ FRAC {a_iを} {B_i})\以来、(。a_iを<10 ^ 5 \)\、明らかに最後の数数の大きさは受け入れることは困難です。
しかし、我々は、配列を開くことができる場合は、\(P-\)と、\(\ FRAC {a_iを} { b_i} \) の添え字として、長いほど、我々実際には毎回\(pは_ {\ FRAC { a_iを} {b_i} } \)プラス\(a_iを\)容易にアレイを維持することができるであろう。
しかし、インデックスの実数でそれは明らかに不可能です。したがって、我々は最初の前にお問い合わせに対処する必要があります\(\ FRAC {} {a_iを b_i} \) 離散へ。
ソートのサイズを比較した場合防止ハング精度にするために、推奨は次に、除算を避けるために、第一クロス乗算スコアを比較することができることに留意されたいです。
セグメントツリー
である私たちは中央値を見つけるに設定(E \)\、および離散化した後、その値がある(K \)\、そして、答えは次のとおりです。
\ [+(\ sum_ {iは= K} ^ N P_I \倍Fact_i-p_k \倍(E \回\ sum_は^ kp_i- \ sum_ {I = 1} ^ K P_I \回Fact_i {iが= 1})\ sum_ ^ np_i {iがkは=})\]
前記\(Fact_iが\)を表す\(I \)真の値を表します。
間隔求め、中央値を見つけ、修正のシングルポイント:その後、我々は4つの操作を可能にするデータ構造を必要とする(\ \和P_Iを)\と間隔を見つける(\ SUM P_I \タイムズFact_i \)\。
だから我々は、ツリーラインを考えます。
音符間隔シーク\(\和P_I \回Fact_i \)は一見難治、我々は長い間、我々は各位置について2つの値を維持することができるように、変形例の一点であるからです。
特に、我々は、セットの事前に定義され、各添加に従ってことを見出した\(a_iを、B_i \) 、提供\(T \)である\(\ FRAC {b_i} { a_iを} \) 離散化後の値、我々は意志\(P_ {T} \)プラス\(a_iを\)は、その後、\(P_T \回Fact_t \)が実際に追加される(a_iを\回Fact_t = a_iを\ \回\ FRAC {b_i} {a_iを} = b_i \) 。
我々は維持している場合、中央値需要のために、実際には、(G = \ SUM P_I \)\、次に中央値は、最初に\(\ lfloor \ FRAC {G + 1} 2 \ rfloor \) 数。
その後、私たちのオンライン部門の木の半分は、あなたが離散中央の値を見つけることができます\(K \) 、その後、あなたが得ることができます(\ e)の\。
特定の実装、可視コードとして。
コード
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 500000
#define LL long long
#define DB double
#define abs(x) ((x)<0?-(x):(x))
using namespace std;
int n,a[N+5],b[N+5],s[N+5],p[N+5];
I bool cmp(CI x,CI y) {return 1LL*b[x]*a[y]<1LL*b[y]*a[x];}
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
int f;char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0,f=1;W(!D) f=c^'-'?1:-1;W(x=tn+(c&15),D);x*=f;}
}F;
class SegmentTree//线段树
{
private:
#define PT CI l=1,CI r=n,CI rt=1
#define LT l,mid,rt<<1
#define RT mid+1,r,rt<<1|1
#define PU(x) (T[x]=T[x<<1]+T[x<<1|1],S[x]=S[x<<1]+S[x<<1|1])
LL T[N<<2],S[N<<2];
public:
I void Upt(CI x,Con LL& y,Con LL& z,PT)//单点修改
{
if(l==r) return (void)(T[rt]+=y,S[rt]+=z);int mid=l+r>>1;
x<=mid?Upt(x,y,z,LT):Upt(x,y,z,RT),PU(rt);
}
I int Qmid(Con LL& rk,PT)//求中位数
{
if(l==r) return l;int mid=l+r>>1;
return rk<=T[rt<<1]?Qmid(rk,LT):Qmid(rk-T[rt<<1],RT);
}
I LL Qtot(CI x,CI y,PT)//区间求和1
{
if(x<=l&&r<=y) return T[rt];int mid=l+r>>1;
return (x<=mid?Qtot(x,y,LT):0)+(y>mid?Qtot(x,y,RT):0);
}
I LL Qsum(CI x,CI y,PT)//区间求和2
{
if(x<=l&&r<=y) return S[rt];int mid=l+r>>1;
return (x<=mid?Qsum(x,y,LT):0)+(y>mid?Qsum(x,y,RT):0);
}
}S;
int main()
{
RI i,k;LL g=0;DB e,w;for(F.read(n),i=1;i<=n;++i) F.read(a[i]);for(i=1;i<=n;++i) F.read(b[i]);//读入
for(i=1;i<=n;++i) a[i]<0&&(a[i]=-a[i],b[i]=-b[i]),s[i]=i;sort(s+1,s+n+1,cmp);//方便起见,将a[i]取正
for(k=0,i=1;i<=n;++i) (!k||(1LL*b[s[k]]*a[s[i]])^(1LL*b[s[i]]*a[s[k]]))&&(s[++k]=s[i]),p[s[i]]=k;//离散化,排序后去重
for(i=1;i<=n;++i)
{
S.Upt(p[i],a[i],b[i]),k=S.Qmid((g+=a[i])+1>>1),e=1.0*b[s[k]]/a[s[k]];//线段树上查询中位数
w=(e*S.Qtot(1,k)-S.Qsum(1,k))+(S.Qsum(k,n)-e*S.Qtot(k,n)),printf("%.8lf\n",w);//利用线段树查询所需信息计算答案
}return 0;
}