前两天刷CF,遇到一题二分的题,觉得挺有意思。
http://codeforces.com/contest/1104/problem/D
这是一道交互题,通过设计询问策略,利用询问的结果来找到一个范围[1,10^9]的一个值,具体的题意:
你可以发出询问,询问的格式为:"? x y", x 和 y值的范围是[1,20^9],每个询问会得到询问结果,分两种情况:
1)"x" 表示 (x mod a) >= (y mod a)
2) "y" 表示 (x mod a) < (y mod a)
其中 a 就是你要寻找的值。通过至多60轮的交互,你必须找出这个值a。
题解
交互式的题首先往二分的方向考虑,60次的限制也印证了二分的方向。关键是怎么分,分析询问给我们带来的信息。
对于x,y,a分析三种情况(我们约定x<y):
1) x < y < a ,这种情况显然返回"y"
2) x<a<=y,这种情况返回不确定,但如果y<=2*x,一定是返回"x"。这个是此题最关键的特性,证明:
对于0<x<a<=y<=2*x,证明 x mod a > y mod a
因为(y mod a) = ((y-a) mod a),且y-a < x <a,
所以 (y mod a) = ((y-a) mod a) = y-a,
(x mod a) = x
因为 y-a<x
所以 (y mod a) < (x mod a) = x
3) a<=x<y,这种情况返回不确定。
也就是说如果a>x的情况下,我们是可以让y=2*x,来判断a的范围是(x,2*x]或(2*x,+∞],二分的思路初见端倪。
上面只讨论了a>x的情况,对于a<=x的情况(也就是情况3)怎么破?
我们可以将a的氛围进行如下划分:
(1,2] 、(2,4] 、(4,8] 、(8,16] .....
从左到右依次判断a的范围在上面的哪个区间,这样就不考虑情况3了,因为在判断区间(x,y]的前提是 a>x,如果a<=x那么一定之前的判断中确定了a的范围。
确定了a的范围为(x,y],那么就可以用二分在这个区间查找到具体a的值,关键是这个区间满足一个特性:对于在这个区间的任意两个值L,R(x<L<R<=y),都满足R <= 2*L。 这个特性决定了对于 mid=(L+R)/2 ,询问 "? L mid"
1)如果返回"x",则a在(L,mid]
2)如果返回"y",a则在(mid,R]
首先用倍增确定一个区间,然后用二分确定具体答案,这个题体现了二分的思想不局限于"分",还有“增”。