HDU 4574 Bombs DFS

题意:

          恐怖分子要制作一批炸弹,做出的炸弹的威力是其原料威力的乘积,并且选取时,后选取的原料威力必须大于等于先选的原料。
          制作出的炸弹按照其原料威力按字典序排序。
          现已知,制作炸弹的原料的威力区间为[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;
}


                       

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值