【GMOJ6800】模拟spongebob
题目
题目链接:https://gmoj.net/senior/#main/show/6800
给出 \(n\) 和两个长度为 \(n\) 的数组 \(a,b\),求
\[\min\{\sum^{n}_{i=1}|a_ix+b_i|\}
\]
其中 \(x\in \mathbb{R}\)。
思路
先把 \(a=0\) 的所有直线的 \(|b|\) 加到答案里。
对于剩余的直线,我们按照他们与 \(x\) 轴交点排序,依次枚举交点区间并计算出此时最优的 \(x\),判断 \(x\) 与当前区间位置分别计算答案即可。
时间复杂度 \(O(n\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=300010;
int n,m;
ll sum,suma,sumb;
double ans;
struct node
{
ll a,b;
double p;
}a[N];
bool cmp(node x,node y)
{
if (x.a==0) return 0;
if (y.a==0) return 1;
return x.p<y.p;
}
int main()
{
freopen("spongebob.in","r",stdin);
freopen("spongebob.out","w",stdout);
scanf("%d",&m); n=m;
for (int i=1;i<=m;i++)
{
scanf("%lld%lld",&a[i].a,&a[i].b);
if (a[i].a!=0) a[i].p=-1.0*a[i].b/a[i].a;
else sum+=abs(a[i].b),n--;
}
sort(a+1,a+1+m,cmp);
for (int i=1;i<=n;i++)
if (a[i].a>0) suma-=a[i].a,sumb-=a[i].b;
else suma+=a[i].a,sumb+=a[i].b;
a[0].p=-10000000000000000.0; a[n+1].p=10000000000000000.0;
ans=10000000000000000.0;
for (int i=1;i<=n+1;i++)
{
double l=a[i-1].p,r=a[i].p,x=-1.0*sumb/suma;
if (l<=x && x<=r) ans=0;
if (x<l) ans=min(ans,suma*l+sumb);
if (x>r) ans=min(ans,suma*r+sumb);
if (a[i].a>0) suma+=2LL*a[i].a,sumb+=2LL*a[i].b;
else suma-=2LL*a[i].a,sumb-=2LL*a[i].b;
}
printf("%.8lf",ans+sum);
return 0;
}