[算法训练] 牛客周赛154-①B-贪心+构造②C-贪心③D-差分+环形结构④E-对顶堆模板
牛客周赛154-①B-贪心+构造②C-贪心③D-差分+环形结构④E-对顶堆模板
牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ
B-贪心+构造
题目
我们把矩阵第 行第 列的位置记为 ,定义两个位置 的曼哈顿距离为 。 请构造一个 行 列的矩阵,满足其中恰好包含 各两个,且这 组相同数字所在位置的曼哈顿距离之和尽可能大。
贪心
首先为了最大化距离和,不会把两个相同数字放在一行。
不同行之间尽可能发在两端,可以得到如下的一个可行的构造。
#include<iostream>using namespace std;int main(){ int n; cin>>n; for(int i=n;i>=1;i--) cout<<i<<' '; cout<<endl; for(int j=1;j<=n;j++) cout<<j<<' '; return 0;}C-贪心
题目
在二维平面中,小红初始位于 。小红会移动 次,每次都只会水平或垂直坐标轴方向移动一个单位距离,我们把这 次移动用一个字符串 表示,具体的: 如果 ,第 次移动小红会从 移动到 ; 如果 ,第 次移动小红会从 移动到 ; 如果 ,第 次移动小红会从 移动到 ; 如果 ,第 次移动小红会从 移动到 。 现在你要删除 中的 个字符。设经过剩余的 次移动后,小红的坐标为 ,你需要最大化 的值。
贪心
对于 (U,D),(L,R)两对,首先肯定是去消除出现次数少的那个,这样可以尽可能增大 ∣x0∣ 或 ∣y0∣。
如果消除玩两对中的较小值后 k 仍然大于 0 ,再去消除较大值。
from collections import Countern, k = map(int, input().split())s = input().strip()cnt = Counter(s)if cnt['U'] >= cnt['D']: k1 = 'U'else: k1 = 'D'if cnt['R'] >= cnt['L']: k2 = 'R'else: k2 = 'L'
kp = {k1, k2}ans = []for ch in s: if ch in kp: ans.append(ch) elif k > 0: k -= 1 else: ans.append(ch)if k > 0: ans = ans[:-k]print(''.join(ans))D-差分+环形结构
题目
给定一个长度为 的环形字符串 ,保证其仅包含 。在环形字符串中, 与 相邻,其中规定 。 你需要依次执行 次如下操作,每次操作给定 : 若 ,反置 ( 变 , 变 ); 若 ,反置 ( 变 , 变 )。 每次操作后,你需要输出当前环形串中相邻且不同的字符对数量。形式化地,你需要求出满足 的下标 的数量,其中 。
差分
区间内部的相邻关系不变(只是 0 变 1,1 变 0,相邻是否相同不变)
只有区间边界处的相邻关系会改变
具体来说,翻转区间 [l, r](环形意义下)后,可能改变的相邻关系只有两个:
(l-1, l):左边界外侧与区间左端(r, r+1):区间右端与右边界外侧
(在环形意义下,下标取模 n)
维护一个数组 diff[i]:
diff[i] = 1:表示s[i]与s[i+1]不同(环形,s[n] = s[0])diff[i] = 0:表示相同
ans = 所有 diff[i] 的和 = 相邻不同字符对的总数。
翻转区间边界时,(idx, idx+1) 这个相邻关系会反转:
- 原来不同 → 现在相同(
diff[idx]从 1 变 0,ans--) - 原来相同 → 现在不同(
diff[idx]从 0 变 1,ans++)
翻转 [l, r] 时,需要翻转两个边界:
- 左边界:
(l-1, l)→flip((l-1+n) % n) - 右边界:
(r, r+1)→flip(r)
情况 1:l <= r(不跨环)
翻转 [l, r],需要处理边界 (l-1, l) 和 (r, r+1)。
特判:如果 l == 0 且 r == n-1,意味着翻转整个环。此时所有相邻关系都反转两次(等价于不变),所以不需要翻转任何边界。
情况 2:l > r(跨环)
翻转 [l, n-1] 和 [0, r],等价于翻转整个环再翻转 [r+1, l-1]。
但简单处理:翻转整个环 = 所有 diff 不变(因为每个相邻关系都反转了),所以等价于只翻转 [r+1, l-1] 的边界。
即只需要处理边界 (r, r+1) 和 (l-1, l)。
特判:如果 l == (r+1) % n,即区间覆盖了整个环(或只有一个元素),不需要翻转。
n, q = map(int, input().split())s = input().strip()diff = [0] * nans = 0for i in range(n): if s[i] != s[(i + 1) % n]: diff[i] = 1 ans += 1def flip(idx): global ans if diff[idx]: diff[idx] = 0 ans -= 1 else: diff[idx] = 1 ans += 1for _ in range(q): l, r = map(int, input().split()) if l <= r: if not (l == 0 and r == n - 1): flip((l - 1 + n) % n) flip(r) else: if l != (r + 1) % n: flip((l - 1 + n) % n) flip(r) print(ans)E-对顶堆模板
题目
给定一个长为 的数组 ,小红可以删除它的任意一个长为 的子数组,我们认为一次删除操作是合法的,当且仅当删除之后数组的中位数恰好为 。请你计算共有多少种不同的合法删除操作,我们认为两次删除操作不同,当且仅当两个子数组的左右端点不同。
在本题中,我们认为空数组的中位数为 。
【名词解释】
子数组:从原数组中,连续的选择一段元素(可以全选、可以不选)得到的新数组。
长度为 的数组的中位数:将所有元素从小到大排列后,位于中间的数。特别地,当 为偶数时,中位数为中间两个数的平均值。例如,数组 的中位数是 ,数组 的中位数是 。
对顶堆
import sysimport heapqfrom collections import defaultdict# 滑动窗口最值板子class SlidingWindowExtreme: def __init__(self, x): self.x = x self.max_less = [] # 小于 x 的最大值(大根堆,存负数) self.min_greater = [] # 大于 x 的最小值(小根堆) self.del_less = defaultdict(int) self.del_greater = defaultdict(int)
def _clean_less(self): while self.max_less and self.del_less[-self.max_less[0]] > 0: val = -heapq.heappop(self.max_less) self.del_less[val] -= 1
def _clean_greater(self): while self.min_greater and self.del_greater[self.min_greater[0]] > 0: val = heapq.heappop(self.min_greater) self.del_greater[val] -= 1
def add(self, v): if v < self.x: heapq.heappush(self.max_less, -v) elif v > self.x: heapq.heappush(self.min_greater, v)
def remove(self, v): if v < self.x: self.del_less[v] += 1 elif v > self.x: self.del_greater[v] += 1
def get_max_less(self): self._clean_less() return -self.max_less[0] if self.max_less else None
def get_min_greater(self): self._clean_greater() return self.min_greater[0] if self.min_greater else None
def solve(): n, k, x = map(int, sys.stdin.readline().split()) a = list(map(int, sys.stdin.readline().split())) m = n - k
# 空数组特判 if m == 0: print(1 if x == 0 else 0) return
# 初始剩余数组 a[k:] less = sum(1 for v in a[k:] if v < x) eq = sum(1 for v in a[k:] if v == x) greater = m - less - eq
# 初始化双堆 heap = SlidingWindowExtreme(x) for v in a[k:]: heap.add(v)
def add_remain(v): nonlocal less, eq, greater if v < x: less += 1 elif v == x: eq += 1 else: greater += 1 heap.add(v)
def remove_remain(v): nonlocal less, eq, greater if v < x: less -= 1 elif v == x: eq -= 1 else: greater -= 1 heap.remove(v)
def check(): if m % 2 == 1: return less < (m + 1) // 2 and greater <= m // 2 else: if eq >= 2: return less < m // 2 and greater < m // 2 elif eq == 0: if less == m // 2 and greater == m // 2: max_l = heap.get_max_less() min_g = heap.get_min_greater() return max_l is not None and min_g is not None and max_l + min_g == 2 * x return False
ans = 0 for l in range(n - k + 1): r = l + k - 1 if l > 0: add_remain(a[l - 1]) remove_remain(a[r]) if check(): ans += 1
print(ans)
if __name__ == "__main__": solve()文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!