ID_8621 最小函数值加强版

难题,膜拜出题人

题目翻译

给定 n 个二次函数,第 i 个二次函数形如 f(x) = a_i x^2 + b_i x + c_i (其中 x 取值范围为整数保证 0 < a_i \le 10^8 , -10^8 \le b_i, c_i \le 10^8

求前 m 小的函数值并输出

解题思路

我们知道二次函数基本长这样(其二次项系数大于 0 )

很明显,它有一个对称轴

我们要找的就是弧线上的点 x 所求出的二次函数的值

它可以在对称轴右边

它也可以在对称轴左边

在所有在弧线上的点 x 所求出的二次函数的值中,在对称轴上的 f(x) 值是最小的

x' 且为函数 f(x) 在对称轴上的 x 的值

则很容易就能想到

f(\lceil x' \rceil)f(\lfloor x' \rfloor) 一定是所有 f(x) 中的最小值和次小值

所以我们有了这样一个思路

a.遍历每个二次函数,对于第 i 个函数,求出 f_i(\lceil x' \rceil)f_i(\lfloor x' \rfloor) 并存入优先队列

b.循环 m 次

\hspace{0.5cm} 1.输出堆顶元素

\hspace{0.5cm} 2.扩展堆顶元素*

[扩展堆顶元素]:此处的扩展堆顶元素指

\hspace{0.5cm} 1.若 x_{top} < x' 且此时堆顶为第 i 个二次函数则将 f_i(x_{top}-1) 存入优先队列

\hspace{0.5cm} 2.若 x_{top} > x' 且此时堆顶为第 i 个二次函数则将 f_i(x_{top}+1) 存入优先队列

玄学

代码

结构体定义

struct node2//优先队列结构体
{
	int x, y, z, now, num;//x,y,z表示构成这个二次函数的三个要素,num表示这个二次函数的值,now表示现在x时多少
	bool b;//b表示这个函数该往哪个方向扩展
};

执行操作

for (int i = 1; i <= n; i++)//执行 a 操作
{
	int x = (-a[i].y) / (a[i].x * 2);
	int ux = x +1, dx = x;
	q.push((node2){a[i].x, a[i].y, a[i].z, ux, a[i].x * ux * ux + a[i].y * ux + a[i].z, 0});
	q.push((node2){a[i].x, a[i].y, a[i].z, dx, a[i].x * dx * dx + a[i].y * dx + a[i].z, 1});
}
for (int i = 1; i <= m; i++)//执行 b 操作
{
	node2 x = q.top();
	q.pop();
	printf("%lld ", x.num);
	if (x.b)
	{
		int dx = x.now - 1;
		q.push((node2){x.x, x.y, x.z, dx, x.x * dx * dx + x.y * dx + x.z, 1});
	}
	else
	{
		int ux = x.now + 1;
		q.push((node2){x.x, x.y, x.z, ux, x.x * ux * ux + x.y * ux + x.z, 0});
	}
}
3 个赞