Guess the number (HDU - 5981 )

传送门

题意:给你一个区间[a,b],让你去猜一个数X,每次可以从区间中选择一个数Y,它会告诉你Y和X的大小关系,如果小于X,它会直接告诉你猜的数小了,如果等于,那么游戏结束,如果大于了X,它只会在第一次给你信息,在这之后,它只会告诉你猜的数是否等于X,让你求最坏情况下,它需要的最小猜测步数和最小猜测步数下的方案数;

题解:

1.这个问题很显然与区间位置无关,只与区间长度有关;

2.考虑下面一个问题,对于一个区间长度为len的区间,我只能通过k次猜出答案,那len最多会是多长?由与你只能猜k次,所以第一次你放在第k个位置是一种最优的选择,因为你只能猜k次,如果当前的左区间长度大于k-1,根据题意如果X小于k,最坏情况下你就必须遍历左区间才能得到答案,次数将会大于k次,而如果左区间长度小于k-1次,那k次之后猜测后的长度是要小于放k时的长度(之后子问题相同),所以第一次取k最优,第二次与第一次同理只是步数变成了k-1步,取的位置是一个前缀合,故猜k次能够猜的最大区间长度为k+k-1+k-2+k-3........,故len=(k+1)*k/2;所以对于一个区间长度只需要找到最小的k满足(k+1)*k/2>=len,k即为最小猜测次数;

3.通过2我们可以知道对于k次猜测,选了第一个位置后,就变成k-1次的子问题,那对于当前区间长度为len,选取了第一个位置后就将区间划分成了两个部分,问题就变成了子区间中长度较大的区间长度,猜测次数在父区间次数上减一的子问题,那次数就是所有子问题的次数合,通过2我们可以求得当前区间长度的最小猜测步数,假定为k,那对于子区间的长度就会有一个限定,即子区间长度不能超过k-1次能够猜出的最大长度,即为ma[k-1]到len-k-1的次数求和(ma[j]记录j次猜,最多能猜多长的区间),维护前缀合后即sum[ma[k-1]]-sum[len-l-1];

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<stack>
#include<vector>
#include<map>
#include<math.h>
#include<stdlib.h>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
const int maxn=5e6+5;
const int mod=100000073;
int rs[maxn],ma[maxn];
ll dp[maxn],sum[maxn];
int main( )
{
    ma[1]=1;
    for(int a=1,b=1;a<maxn;a++){
        if((b+1)*b/2>=a)
            rs[a]=b;
        else{
            b++;
            ma[b]=(b+1)*b/2;
            rs[a]=b;
        }
    }
    sum[0]=1;
    dp[1]=1,sum[1]=2;
    for(int a=2;a<maxn;a++){
        dp[a]=(sum[ma[rs[a]-1]]-sum[a-rs[a]-1]+mod)%mod;
        sum[a]=(sum[a-1]+dp[a])%mod;
    }
    int l,r;
    while(~scanf("%d%d",&l,&r)){
        printf("%d %lld\n",rs[r-l+1],dp[r-l+1]);
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值