洛谷P2085 最小函数值(优先队列)

题意

给定\(n\)个二次函数:\(f(x)=ax^2+bx+c\),保证\(a,b,c,x\)均为正整数且\(a\leq10,b\leq100,c\leq10000\)。请输出前\(m\)大的函数值。

有点难说,直接上题目链接吧

暴力

显然,\(f(x)\)是单调递增函数,肯定要用到堆(优先队列)。

直接每个函数把前\(m\)个扔进去,然后在整个队列里面输出前\(m\)个就行了。这种复杂度为\(O((nm)log(nm))\),显然超时。

优化

不妨令\(g(x)=x^2+x+1,h(x)=10x^2+100x+10000\),则\(g(x)\leq f(x)\leq h(x)\)

假设\(g(x)\)的第k项必然大于所有函数值的总集合构成的单调递升数列的第m项,那么\(g(k)>h(\frac{m}{n})\)

解得\(k\geq\sqrt{10(\frac{m}{n})^2+100\frac{m}{n}+9999}\)(必要解,非充分解)

综上可以将复杂度压至\(O(T log T)\),其中\(T=kn=\sqrt{10m^2+100mn+9999n^2}\leq 10^6\),降到了一个可以接受的范围内。

代码如下:

#include<bits/stdc++.h>
using namespace std;
const int N=10010,M=10010; 
priority_queue<int,vector<int>,greater<int> >q;
int n,m,A[N],B[N],C[N];
inline int calc(int i,int x){
	return x*(A[i]*x+B[i])+C[i];
}
int main()
{
	//read
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i)
		scanf("%d%d%d",&A[i],&B[i],&C[i]);
		int k=sqrt(10*m*m/n/n+100*m/n+9999)+1;
	//calc
	for(int x=1;x<=k;++x)
		for(int i=1;i<=n;++i)
			q.push(calc(i,x));
	//print
	for(int i=1;i<m;++i){
		printf("%d ",q.top());
		q.pop();
	}
	printf("%d",q.top());
	return 0;
}

正解

都将\(f_i(1)\)扔进堆里面,然后取出最小的那项,然后看下它是第几个函数,然后将它的下一个后继(例如\(f_i(x)\)的下一个后继是\(f_i(x+1)\))扔进去,以此类推,取满\(m\)个为止。

代码略,洛谷题解里面蛮多的(逃)。

posted @ 2020-12-23 00:02  cyhforlight  阅读(93)  评论(0编辑  收藏  举报