题意:给你一个区间[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]);
}
}