【洛谷2085】最小函数值(堆)
点此看题面
大致题意: 给你\(n\)个形如\(F_i(x)=A_ix^2+B_ix+C_i(A_i,B_i,C_i,x∈N^*)\)的函数,请你求出所有函数的所有函数值中最小的前\(m\)个值。
最暴力的解法
显然,我们可以发现一个性质:对于每一个函数,它的值肯定随着\(x\)的增大而增大。
也就是说,最终答案的\(x\)肯定小于\(m\),我们就可以考虑对于每一个函数,求出\(x=[1..m]\)时的值,然后将所有的值排序一遍,输出前\(m\)小的即可。
考虑如何优化
由于上面的那个性质,我们可以发现,对于同一个函数,如果\(x=k\)时的值是最终的一个答案,那么\(x=k-1\)时的值显然也是最终的一个答案,因为\(x=k-1\)时的值是小于\(x=k\)时的值的。
推广可得,如果\(x=k\)时的值是最终的一个答案,那么\(x=[1..k]\)时的值都是最终的答案。
既然这样,我们又可以得到,只有\(x=k-1\)时的值是最终的一个答案了,\(x=k\)时的值才有可能是最终的一个答案。
这样一来,我们就可以用堆(优先队列)来进行优化了。
正确的做法:堆
首先,我们将每一个函数\(x=1\)时的值加入堆中,然后,进行\(m\)次操作,每次操作取出堆顶的值并将其输出,然后将当前函数的\(x\)加\(1\)后得到的值重新加入堆中。
我们可以发现,在整个过程中,对于每一个值我们只需要记录三个信息:值的大小、属于哪一个函数以及当前的\(x\)。
因为每一次操作的时间复杂度是\(O(logn)\)的,因此总复杂度是\(O(mlogn)\)。
代码
#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define tc() (A==B&&(B=(A=ff)+fread(ff,1,100000,stdin),A==B)?EOF:*A++)
#define pc(ch) (pp_<100000?pp[pp_++]=(ch):(fwrite(pp,1,100000,stdout),pp[(pp_=0)++]=(ch)))
#define fi first//减少代码量,用fi表示第一个元素,se表示第二个元素,th表示第三个元素
#define se second.first
#define th second.second
#define N 10000
int pp_=0;char ff[100000],*A=ff,*B=ff,pp[100000];
using namespace std;
int n,m,a[N+5],b[N+5],c[N+5];
typedef pair<int,pair<int,int> > Status;//对于每一个值,需要记录3个信息
priority_queue<Status,vector<Status>,greater<Status> > q;//一个堆(优先队列)
inline void read(int &x)
{
x=0;int f=1;static char ch;
while(!isdigit(ch=tc())) f=ch^'-'?1:-1;
while(x=(x<<3)+(x<<1)+ch-48,isdigit(ch=tc()));
x*=f;
}
inline void write(int x)
{
if(x<0) pc('-'),x=-x;
if(x>9) write(x/10);
pc(x%10+'0');
}
int main()
{
register int i;
for(read(n),read(m),i=1;i<=n;++i)
read(a[i]),read(b[i]),read(c[i]),q.push(make_pair(a[i]+b[i]+c[i],make_pair(i,1)));//将每个函数x=1时的值加入堆中
for(i=1;i<=m;++i)
{
Status k=q.top();q.pop();//取出堆顶
write(k.fi),pc(' '),++k.th,q.push(make_pair(k.th*k.th*a[k.se]+k.th*b[k.se]+c[k.se],make_pair(k.se,k.th)));//输出堆顶的值并将该函数x+1时的值重新加入堆中
}
return fwrite(pp,1,pp_,stdout),0;
}
待到再迷茫时回头望,所有脚印会发出光芒