洛谷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\)个为止。
代码略,洛谷题解里面蛮多的(逃)。