Codeforces_Round_616_(Div_2)_C題

  • 2020 年 2 月 18 日
  • 筆記

題目大意

有$n$個人,有$n$個數字組成序列$a$, 你當前站在第$m$個位置,每一次每個人從這$n$個數字的頭或者尾拿走一個數字,一開始你可以說服在拿的時候$k$個人拿首還是拿尾,其他人會任意拿,說服那些人拿首還是拿尾要一開始就確定好,中間不能變。最大化通過控制能確定拿到的值

題解

註:題解描述下標從1開始,程式碼中下標從0開始

顯然對於後$n-m$個人,他們怎麼拿對結果都沒有影響。那麼可以說服的人數$k = min(k, m-1)$,假設說服$x$個人拿首,有$y$個沒說服的人拿了首,顯然$x in [0, k],y in [0, m-1-k]$,那麼容易知道第$m$個人拿的時候,頭是$a_{x+y+1}$,因為首拿了$x+y$個,尾是$a_{x+y+1+n-m}$ 因為輪到第$m$個人拿的時候,還有$n-m+1$個數字,那麼$z-(x+y+1)+1=n-m+1$,解得$z=x+y+1+n-m$。所以暴力枚舉$x$和$y$的值就得到一個$O(n^2)$的演算法。最終答案如下

$$b_i=max(a_{1+i}, a_1+i+(n-m))$$

$$ans = max_{x in [0, k]}lbrace min_{y in [0, m-1-k]}b_{x+y} rbrace$$

# def wrapper(func):  #     def inner():  #         return next(func)  #     return inner  # input = wrapper(open('in.txt'))    T = int(input())  for case in range(T):      n, m, k = map(int, input().strip().split())      a = list(map(int, input().strip().split()))      k = min(m-1, k)      ans = 0      for x in range(k+1):          res = int(2e9)          for y in range(m-k):              res = min(res, max(a[x+y], a[x+y+n-m]))          ans = max(ans, res)      print(ans)  '''    '''

令$y』=x+y$,上面的式子可以變化為

$$ans = max_{x in [0, k]}lbrace min_{y』 in [x, x+m-1-k]}b_{y』} rbrace$$

所以上面的式子可以用線段樹來計算,複雜度是$O(nlogn)$,由於求區間最小值的時候區間長度是$m-k$不變的,所以還可以用單調隊列來做(和LeetCode的這道題目差不多239. Sliding Window Maximum),複雜度是$O(n)$,下面是單調隊列的做法:

# def wrapper(func):  #     def inner():  #         return next(func)  #     return inner  # input = wrapper(open('in.txt'))    import collections    T = int(input())  for case in range(T):      n, m, k = map(int, input().strip().split())      a = list(map(int, input().strip().split()))      k, b = min(m-1, k), []      for i in range(m):          b.append(max(a[i], a[i+n-m]))      ans = 0      d = collections.deque()      for i in range(m):          while d and b[i] <= b[d[-1]]:              d.pop()          while d and (i-d[0]+1 > m-k):              d.popleft()          d.append(i)          if i >= m-k-1:              ans = max(ans, b[d[0]])      print(ans)  '''    '''