CF1896 E,F,G 题解
E:
题意:给出排列a,每次数字与下标不匹配的所有数会循环右移一位,问每一个数什么时候到自己的下标位置。
第一眼以为又是一道On链表,但是发现可能经过好多轮才有数字归位,于是放弃了“每次移动会使得那些数字归位”这个思路。
Solution:
发现答案不超过n,因为你移动n次相当于转了一个圈,肯定能回到自己的位置,答案只会比你到自己位置的距离要小,小多少呢?
如果某个数在回家的过程中遇到了一个已经归位的数字,它会跨过这个数字,于是回家的时间就会减一。
它可以遇到多少个已经归位的数字,即是从它起点位置L,到它该到的终点位置R区间内,有多少个起点和终点都在区间内的数。具体的:L即为当前数位置i,R为 \(a_i\),如果数字 \(a_i\) 需要跨过一次编辑,就让R变成 \(a_i+n\)。
这样每个点有一个区间[L,R],并统计每个区间内包含了多少个小区间,L和R分别想象成x轴和y轴,变成了经典的二维数点问题。
F:
题意:妙妙构造题,给出01序列,一个长度为n的括号操作序列表示每个括号翻转一次,10个以内括号操作序列来将原01序列变回纯0。
之前有过一个思路,虽然行不通但是值得思考:把 “(” 当做这个位置+1,把 ")" 当做下一个位置-1,那么每个数的翻转次数就是所有括号操作序列在当前位置的二维前缀和,根据奇偶性来构造当前位置的所有括号。但是因为括号本身需要左右配对,思考了很久发现还是走不太通。
Solution:
发现网上的题解都一个样,应该是官方题解。
首先解决细节:
- 每个操作不会改变01序列的奇偶性,因此初始必须有偶数个1。
- 括号序列第一位和最后一位肯定只有一层括号,因此这两位每次变化都是相同的,初始也必须相同。
然后是接下来的妙妙处理,这题并没有按位数奇偶来讨论,这是按相邻两个作为一组来进行分组。
对于"10000001"这样两个 1 之间有偶数个 0 的情况,可以用 "(()()())" 来解决,那么我们可以把原数列转化成1001100001这种,每两个1直接都有偶数个0,我们发现不符合这种格式的数列,是因为第 2k 和第 2k+1 位可能不同,如果所有的 2k 和 2k+1 位相同,就可以符合上面这种形式。
那么就将中间的相邻两位 2k 和 2k+1 分一组,对于数字一样的相邻两位,我们用 "()",数字不同的两位,我们用 "((" 或 "))",这样就可以扭正相邻两位。
比如:"1001011101" 我们就用 "( () (( () )) )"进行相邻两位修正。
修正可能会使首尾两位变成0,我们再来一次 "()()()()" 就能翻转序列,然后第三次用上面所说的 "(()()())" 格式来解决。因此这题最多用三次操作就可了。
G:
题意:妙妙交互题,\(n^2\) 个人跑步速度各不同,每次询问 n 个不同的人,评测机返回 n 个人中跑的最快人的编号,请在 \(2n^2-2n+1\) 次询问内给出前 \(n^2-n+1\) 个人的准确排名。n<=20
\(n^2-n+1\) 是因为最慢的 \(n-1\) 个人没法测出,但是不知道询问次数是怎么来的,别急,我们先想出简单点的方法,再往这个数字靠拢。
Solution:
这是一道非常有实际意义的题,经常军训的小伙伴应该都会发现,这个和教官给方阵排高低个儿非常相似,一开始方阵是乱站的,你每次只能从一整行或者一整列中,看出最高的那个人,你该怎么从高到低排好所有同学的个头呢?
前 n+1 步比较好想,先把每列的最高个挑出来,我们把他们调整到方阵的第一排,再从第一排中挑出最高者,就是所有同学的最高者。把这个同学揪到方阵外面。
第二高该怎么找,第二高这时不一定在第一排,也有可能是在刚刚我们挑出最高者的那一列,所以我们再次对那一列进行筛选,找出这列新的最高者,让他走到第一排,这时第二高一定在第一排了,从第一排中挑出最高者,就是所有同学的第二高。把他揪到方阵外。
第三高,第四高,都能这么找,每次挑一个同学需要耗费教官两次比较。这个挑选方式至少和我们答案的数量级一样了。
这题和上面所讲的方式有个不一样的细节,就是每次必须正好n个同学进行比较。因此,在最高者被揪到方阵外之后,他的那一列会少一个人,我们就从其他列里随便抓一个人出来补这个洞,即使这个人肯定不是新的最高者。
之后每次诞生最高者,我们需要补的可能不止一人,而是需要去补好多人,但这个不影响新的最高者的寻找。
我们一直能够用这个方式找到只剩 2n-1 个同学,即一行一列,这时若揪出去一个同学,这个同学的列就不能补齐了。在这之前,我们用了 \(n+1+2*(n^2-2n) +1 = 2n^2-3n+3\) 次。(左式最后的加一是因为找到最后2n个同学的最高者后还要对该列进行一次筛选)
我们还剩 n-1 次,用于选出 2n-1 个人中的前 n 高。
这时,一个重要的结论出现了:第一排的所有同学就是 2n-1 个人中的前 n 高。
为什么呢,乍一想可能想不出来,但是反过来思考:如果你是最矮的那 n-1 个人,因为每次列选时都是 n 个人比高低,所以你永远也不会被教官放到第一排哈哈。所以,现在第一排的 n 个同学就是前 n 高的,最矮的 n-1 个人还在后面站着呢。
那么就很简单了,n-1 次每次可以找出最高者,最后一个人,用排除法就确定了。
把所有的数放到方阵里,把自己想象成指挥每位同学移动的教官,实时调整每个数字的位置,代码也就好写多了。