描述
其中系数aj都是整数满足0≤aj≤1000且至少有两个系数严格大于0,分别将n=1,n=2,n=3n...代入以上函数可以得到一个无穷长度的整数序列,即用8个系数a7,a6...a0可以唯一确定一个无穷长度的整数序列,现在给出k个通过以上方法定义的无穷序列,你需要求出将这些序列所有数字放在一起后,第n小的数字是多少?
输入描述:
第一行包含一个整数k,1≤k≤104
接下来k行,每行包含8个整数a7,a6,.....a0,表示一个函数的系数,0≤aj≤1000
最后一行包含一个整数n,1≤n≤105
输出描述:
输出对应的答案,保证最后的答案不超过10的17次方
示例1
输入:
3 0 0 0 0 1 2 0 0 0 0 0 0 0 0 10 6 0 0 0 0 0 0 25 1 9输出:
51
这道题第k最小值问题,常用的解法有排序,堆排序等,这道题最合理的是小根堆解法,堆结构维持一个从小到大排序的堆结构,根据题意可以得到,每个方程式呈现递增的趋势,而且变量n是正整数,当n等于1可以计算出所有方程组的值,但有可能还没办法算法最小的k个值,这时候需要找出最小的那个值,然后让n=2,进行计算,因此需要变量记录当前最小值所对应的方程式id,这里可以利用一个节点存储Node,包括当前的值val以及对应的方程式id。每次获取最小值,然后进行计算第二最小值,这种思想利用到K并归路的最小堆。
#include <bits/stdc++.h>
using namespace std;
//各个方程组是递增序列
struct Node{
long val,id;//当前值,以及对应的方程组
};
struct cmp{
bool operator()(const Node& v1,const Node& v2){
return v1.val>v2.val;
}
};
//方程式计算
long getValue(vector<int>& seq,int n){
long ret=seq[0];
//f=((((((a7*n +a6)*n +a5)*n +a4)*n +a3)*n +a2)*n +a1)*n +a0
for(int i=1;i<8;++i){
ret=ret*n+seq[i];
}
return ret;
}
int main(int argc,char* argv[]){
int k,n;
cin>>k;
vector<vector<int>> seq(k,vector<int>(8));
for(int i=0;i<k;++i){
for(int j=0;j<8;++j){
cin>>seq[i][j];
}
}
cin>>n;
priority_queue<Node,vector<Node>,cmp> q;
//最小堆K路归并
vector<int> next(k,1);//方程式中n值从1开始
for(int i=0;i<k;++i){
long val=getValue(seq[i],next[i]);
q.push({val,i});
++next[i];//更新方程式的n值
}
//小根堆
while(--n){
auto cur=q.top();//取出最小值
q.pop();
long val=getValue(seq[cur.id],next[cur.id]);
q.push({val,cur.id});
++next[cur.id];
}
//第n个小值
cout<<q.top().val<<endl;
return 0;
}