hdu4574Bombs(枚举+dfs)

题目请戳这里

题目大意:给一个区间[A,B],表示有B - A + 1种火药,火药i的威力值为i,现在要用这些火药做炸弹,每次按原料的字典序做炸弹。每个炸弹的威力值是每种原料威力值的乘积。现在给一个区间[L,R]表示炸弹的威力值范围,求第K个炸弹的配方。

题目分析:因为炸弹的威力值是原料威力值的乘积,R不超过10^9,A至少为2,所以极端情况下炸弹至少要30种原料,所以直接搜就可以了。

首先根据给定的2个区间,求出重合的区间,表示只用一种原料能做的炸弹有多少,如果K满足这个范围,直接输出答案。

然后根据[L,R]区间确定所要原料的数量的上下界。然后枚举原料长度,dfs。

主要的剪枝是,当搜到第i中火药的时候,假设剩下的全部用威力值最大的B种火药,如果还不能达到炸弹威力值下界L,退出;假设剩下的全部用威力值最小的A种火药,已经超过炸弹威力值上界R,退出。

不过我加了这个剪枝还是TLE,后来又加了个小剪枝,就是搜到倒数第二层的时候,最后一层的情况直接统计,不用去搜最后一层了。

详情请见代码:

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef __int64 ll;
ll A,B,L,R,K;
ll path[400];
bool flag;

ll pow(ll a,ll b)
{
    ll ret = a;
    for(int i = 1;i < b;i ++)
        ret *= a;
    return ret;
}

void dfs(ll cur,int deep,int dp,ll po)
{
    if(flag)
        return;
    path[dp] = cur;
    if(dp + 1 == deep)//倒数第二层
    {
        ll ta = L/po;
        if(L%po)
            ta ++;
        ll tb = R/po;
        ll tmp;
        if(tb < cur)
            tmp = 0;
        else
        {
            if(tb <= B)
            {
                if(ta < cur)
                    tmp = tb - cur + 1;
                else
                    tmp = tb - ta + 1;
            }
            else
            {
                if(ta < cur)
                    tmp = B - cur + 1;
                else if(ta <= B)
                        tmp = B - ta + 1;
                    else
                        tmp = 0;
            }
        }
        K -= tmp;
        if(K > 0)
            return;
        else
            K += tmp;
    }
    ll i;
    if(dp == deep)
    {
        if(po >= L && po <= R)
            K --;
        if(K == 0)
        {
            printf("%I64d\n",po);
            for(i = 1;i < dp;i ++)
                printf("%I64d ",path[i]);
            printf("%I64d\n",path[i]);
            flag = true;
        }
        return;
    }
    i = L/po;
    i = i / (pow(B,(deep - dp - 1)));
    if(i < cur)
        i = cur;
    for(;i <= B;i ++)
    {
        if(pow(i,(deep - dp)) * po > R)
            return;
        dfs(i,deep,dp + 1,po * i);
    }
}

int main()
{
    int t,cas = 0;
    ll i,j;
    scanf("%d",&t);
    while(t --)
    {
        scanf("%I64d%I64d%I64d%I64d%I64d",&A,&B,&L,&R,&K);
        printf("Case #%d: ",++cas);
        int cros = 0;
        if(R < A)
        {
            printf("-1\n");
            continue;
        }
        ll tans;
        if(R <= B)
        {
            if(L < A)
            {
                cros = R - A + 1;
                if(cros >= K)
                    tans = A + K - 1;
            }
            else
            {
                cros = R - L + 1;
                if(cros >= K)
                    tans = L + K - 1;
            }
        }
        else
        {
            if(L < A)
            {
                cros = B - A + 1;
                if(cros >= K)
                    tans = A + K - 1;
            }
            else if(L >= A && L <= B)
                 {
                     cros = B - L + 1;
                     if(cros >= K)
                        tans = L + K - 1;
                 }
                else
                    cros = 0;
        }
        if(cros >= K)
        {
            printf("%I64d\n%I64d\n",tans,tans);
            continue;
        }
        K -= cros;
        ll l,r;
        l = r = 2;
        ll ta,tb;
        ta = A;
        tb = B;
        while((ta*A) < R)
        {
            r ++;
            ta = ta * A;
        }
        while((B*tb) < L)
        {
            l ++;
            tb = tb * B;
        }
        flag = false;
        for(i = l;i <= r && !flag;i ++)
        {
            j = L / (pow(B,i - 1));
            if(j < A)
                j = A;
            for(;j <= B && !flag;j ++)
            {
                if(pow(j,i) > R)
                    break;
                dfs(j,i,1,j);
            }
        }
        if(!flag)
            printf("-1\n");
    }
    return 0;
}
/*
100
2 2 1 4 1
2 5 1 4 4
73 23642 12 20903 29401
2 50 1 1000000000 815180
2 1000000 2 1000000000 1000000
2 100000 2 1000000000 1000000
2 10000 2 1000000000 1000000
2 1000 2 1000000000 1000000
2 100 2 1000000000 1000000
2 100000 1000000 1000000000 1000000
*/
/*
ans:
2
2
4
2 2
-1
59200
4 4 5 20 37
4
2 2
616
11 56
510151
101 5051
27
3 3 3
679728
7 17 68 84
3046246
31 98266
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值