题解/算法 {AtCoder355/E - Guess the Sum}
@LINK: https://atcoder.jp/contests/abc355/tasks/abc355_e
;
首先 读懂题 并且把他简化 非常重要, 因为题目中涉及了l,r, I, J
看着就很复杂, 明明是查询query(l,r)
区间 但是 你还要把他转换成I,J
来输出, 因此 你要看到query(l,r)
的本质是什么: l=(1<<I)*J, r=l+(1<<I)-1
这说明: r-l+1
的长度 是1<<I
也就是说 查询的区间长度为1<<I
二次幂, 其次还有一个条件 即l % len == 0
即l
是区间长度的倍数; 简而简之, 查询的区间长度是二次幂 左端点是区间长度的倍数;
因此, 对于整个数组的范围[0, R] (R==(1<<N)-1)
:
+(查询区间长度为1): 把[0,R]
从[0]
开始 划分为若干个长度为1的子区间 即[0-0] [1-1] [2-2] [3-3] ...
;
+(查询区间长度为2): 把[0,R]
从[0]
开始 划分为若干个长度为2的子区间 即[0-1] [2-3] [4-5] [6-7] ...
;
+(查询区间长度为4): 把[0,R]
从[0]
开始 划分为若干个长度为4的子区间 即[0-3] [4-7] ...
;
+(查询区间长度为8): 把[0,R]
从[0]
开始 划分为若干个长度为8的子区间 即[0-7] [8-15] ...
;
…
+(查询区间长度为1<<N
): 则只有[0, R]
;
.
其实你仔细观察下 这些可以查询的区间, 就是 区间[0,R]
所对应的线段树区间 (区间长度为1的这些区间 就对应线段树的叶子节点); (不过这个题的正解 虽然与线段树无关…);
注意, 题目要求的是 最小查询次数;
错误做法: 将[L,R]
拆分成若干个子区间, 他们之和 就是答案;
. .
错误, 因为题目要求最小查询次数, 比如[0,1,2,...,7,...]
比如L=1,R=7
按照你的贪心 答案是[1,2] + [3,3] + [4-7]
而正解是 [0,7] - [0,0]
;
也就是 会涉及到区间相减的情况, 比如答案区间[L,R]
他对应若干个区间[li, ri]
, 我们要将每个区间 添加一个符号+1 / -1
, 使得这些区间之和 是等于[L,R]
的(换句话说[L,R]
里每个元素出现1次 而<L || >R
的元素 都出现0次), 这就是新的问题模型, 可是要怎么做呢?
#错误#: 容易想到前缀和 因为他可以处理区间相减, 但是 前缀和只能求[l,r] = [0,r] - [0,l-1]
, 而这个问题 [l,r]
的答案 是会涉及到>r
的区间的 (比如L=0, R=6]
答案是[0-7] - [7,7]
), 换句话说 这个题目 是由后效性的 而前缀和是无后效性的 即在[i]
处 只有<i
是正确的;
重新回到正确思路来