1. 程式人生 > 實用技巧 >JZOJ 6800.NOIP2020.9.19模擬spongebob

JZOJ 6800.NOIP2020.9.19模擬spongebob

題目大意

求形如

\[\sum_{i=1}^n |a_ix + b_i| \]

的最小值

思路

我們顯然可以先把係數 \(a\) 提出來
於是就成了 \(\sum_{i=1}^n |a_i|·|x + \frac{b_i}{a_i}|\)
對於任意一個 \(i\),它的零點在 \(-\frac{b_i}{a_i}\)
而我們知道整個函式的最值必然在零點處
那麼取所有零點計算取最小值就可以了
\(O(n^2)\)
其實並不需要那麼高的複雜度
我們先將零點從大到小排序
然後換一個零點時,它後面的式子小於零,前面的式子大於零,用它的增量就可以 \(O(1)\) 計算了

注意:\(a_i\) 可能為零!需特別處理

\(Code\)

#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;

const int N = 3e5 + 5;
int n , m;
struct node{
	double a , b;
}c[N + 5];
double val , ans , k1 , k2;

bool cmp(node x , node y){return x.b < y.b;}

int main()
{
	freopen("spongebob.in" , "r" , stdin);
	freopen("spongebob.out" , "w" , stdout);
	scanf("%d" , &n);
	for(register int i = 1; i <= n; i++) 
	{
		++m;
		scanf("%lf%lf" , &c[m].a , &c[m].b);
		if (c[m].a == 0) {val += abs(c[m].b); --m; continue;}
		c[m].b = -c[m].b / c[m].a , c[m].a = fabs(c[m].a);
	}
	sort(c + 1 , c + m + 1 , cmp);
	for(register int i = 2; i <= m; i++) val += (c[i].b - c[1].b) * c[i].a , k1 += c[i].a;
	k2 = c[1].a , ans = val;
	for(register int i = 2; i <= m; i++)
	{
		val -= k1 * (c[i].b - c[i - 1].b);
		val += k2 * (c[i].b - c[i - 1].b);
		k1 -= c[i].a , k2 += c[i].a;
		ans = min(ans , val); 
	}
	printf("%lf\n" , ans);
}