题意:
恐怖分子要制作一批炸弹,做出的炸弹的威力是其原料威力的乘积,并且选取时,后选取的原料威力必须大于等于先选的原料。
制作出的炸弹按照其原料威力按字典序排序。
现已知,制作炸弹的原料的威力区间为[l,r],期望得到的炸弹的威力区间为[a,b],求第K的炸弹的威力及原料组成。
输入:
T组数据,对于每组数据c,给出五个数a b l r K
思路:
注意到数据的范围
2<=A <= B<=10^6
1<=L<=R<=10^9
1<=K<=10^6
最直接的想法是按层序建立一颗字典树,同时检查每个节点是否可以制成炸弹。
显然这样会产生许多多余的无效节点,在生成1e6个有效节点的情况下,一定会MLE。
我们要对方法进行改进
我们可以不建立字典树,只统计每一层的有效节点数。
这样有一个问题
层与层之间的节点不具有继承性
即:假如在第2层的某个节点是无效的,但它在第3层的子节点是有效的。
所以对于每一层的有效节点数我们要单独计算,即枚举层数。
并且我们注意到炸弹最大威力为1e9,原料的最小威力为2,所以至多有ceil(log2 1e9) <= 30 层。
枚举时如何搜索?
搜到X层原料的时候
假设剩下的全部用威力最大的B种原料
我们可以得到,当前层使用原料的威力上界。
(超过上界的原料最终配成的炸弹会超过威力上限)
同理,假设剩下的全部用威力最小的A种原料
我们可以得到,当前层使用原料的威力下界。
(超过上界的原料最终配成的炸弹会超过威力下限)
搜索的同时记录当前用了那种原料并比较有效节点数是否已达到K即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int LIM=1e9;
const int INF=0x3f3f3f3f;
int bob[40],ansdepth,k;
int pow(int a,int b){
long long ans=1;
for(int i=1;i<=b;i++){
ans*=a;
if(ans>=LIM||ans<0) return INF;
}
return ans;
}
void get_num(int tot,int depth,int a,int b,int l,int r){
int down=ceil(((double)l)/pow(b,tot-depth));
down=max(a,down);
int up=r/pow(a,tot-depth);
up=min(b,up);
if(down>up) return;
int cnt=up-down+1;
if(depth==tot){
if(k<=cnt){
bob[depth]=down+k-1;
ansdepth=depth;
return ;
}
k-=cnt;
}else{
for(int i=down;i<=up&&ansdepth==-1;i++){
bob[depth]=i;
int newl=ceil(((double)l)/i);
int newr=r/i;
if(newl<=newr) get_num(tot,depth+1,i,b,newl,newr);
}
}
return ;
}
void solve(){
int a,b,l,r;
scanf("%d%d%d%d%d",&a,&b,&l,&r,&k);
ansdepth=-1;
for(int tot=1;tot<=30;tot++){
get_num(tot,1,a,b,l,r);
if(ansdepth!=-1) break;
}
return ;
}
int main(){
int T;
scanf("%d",&T);
for(int Case=1;Case<=T;Case++){
solve();
printf("Case #%d: ",Case);
if(ansdepth==-1){puts("-1");continue;}
int tot=1;
for(int i=1;i<=ansdepth;i++)
tot*=bob[i];
printf("%d\n",tot);
for(int i=1;i<=ansdepth;i++){
printf("%d",bob[i]);
if(i==ansdepth) puts("");
else printf(" ");
}
}
return 0;
}