[THUPC2019] 不等式

题目

这个\(|a_ix+b_i|\)看起来不是很优美,我们考虑把它变得优美一些;

提一个\(a_i\)出来就是\(|a_i||x+\frac{b_i}{a_i}|\),我们把\(\frac{b_i}{a_i}\)看成\(-(-\frac{b_i}{a_i})\),于是原来的柿子就是\(|a_i||x-(-\frac{b_i}{a_i})|\)

啊,两个坐标相减取绝对值不就是距离吗;于是我们把问题转化成了有一条数轴,\((-\frac{b_i}{a_i},0)\)处有\(|a_i|\)个点,我们需要找到一个\(x\),使得\((x,0)\)到所有点的距离和最小。

这是一个经典问题,\(x\)应该为这些坐标的中位数,这时距离和最小,调整法可证;

于是我们把坐标离散化后用线段树来维护数轴,找中位数直接在线段树上二分即可。

代码

#include<bits/stdc++.h>
#define re register
#define db double
#define LL long long
const int maxn=5e5+5;
inline int read() {
	char c=getchar();int x=0,r=1;while(c<'0'||c>'9'){if(c=='-')r=-1;c=getchar();}
	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48,c=getchar();return x*r;
}
int a[maxn],b[maxn],id[maxn],pos[maxn],p[maxn],n;
int l[maxn*3],r[maxn*3];LL sa[maxn*3],sb[maxn*3],sum;
inline int cmp(int A,int B) {return 1ll*b[A]*a[B]>1ll*b[B]*a[A];}
void build(int x,int y,int i){
	l[i]=x,r[i]=y;if(x==y){p[x]=i;return;}
	int mid=x+y>>1;build(x,mid,i<<1),build(mid+1,y,i<<1|1);
}
inline void chg(int x,int va,int vb) {x=p[x];while(x)sa[x]+=va,sb[x]+=vb,x>>=1;}
int fid(int i,LL nw) {
	if(l[i]==r[i])return l[i];
	return sa[i<<1]>=nw?fid(i<<1,nw):fid(i<<1|1,nw-sa[i<<1]);
}
LL qry(int x,int y,int i,int o) {
	if(x<=l[i]&&y>=r[i]) return !o?sa[i]:sb[i];
	int mid=l[i]+r[i]>>1;LL tot=0;
	tot=(x<=mid?qry(x,y,i<<1,o):0)+(y>mid?qry(x,y,i<<1|1,o):0);
	return tot;
} 
int main() {
	n=read();for(re int i=1;i<=n;i++)a[i]=read();for(re int i=1;i<=n;i++)b[i]=read();
	for(re int i=1;i<=n;i++)if(a[i]<0)a[i]=-a[i],b[i]=-b[i];
	for(re int i=1;i<=n;i++)id[i]=i;std::sort(id+1,id+n+1,cmp);
	for(re int i=1;i<=n;i++)pos[id[i]]=i;build(1,n,1);
	for(re int t,i=1;i<=n;i++) {
		chg(pos[i],a[i],-b[i]);t=fid(1,((sum+=a[i])+1)>>1);
		db pnw=(db)(b[id[t]])/(db)(-a[id[t]]),ans=0;
		ans=(db)qry(1,t,1,0)*pnw-qry(1,t,1,1)+qry(t,n,1,1)-(db)qry(t,n,1,0)*pnw;
		printf("%.10lf\n",ans);
	}
	return 0;
}
posted @ 2020-01-14 07:56  asuldb  阅读(161)  评论(0编辑  收藏  举报