牛客周赛145-图论/生成树/DSU+构造
牛客周赛145-补EF
https://ac.nowcoder.com/acm/contest/134981
Easy:A、B、C
Mid:D
Hard:E、F
A-Counter
Counter计数即可
from collections import Counters = input().strip()cnt = Counter(s)if len(cnt) == 2: print("Yes")else: print("No")B-滑动窗口
题意:
给定一个01串,求该01串的子串中满足同时包含0和1的数目
由于是01串,满足题意的答案即为:总子串的数量减去单子串的数量即可
使用的f(k)函数统计字符串 s 中所有不同字符个数不超过 k 的子串数量
from collections import defaultdictdef f(k): if k <= 0: return 0 cnt = defaultdict(int) left = 0 d = 0 ans = 0 for right in range(len(s)): c = s[right] cnt[c] += 1 if cnt[c] == 1: d += 1 while d > k: c_left = s[left] cnt[c_left] -= 1 if cnt[c_left] == 0: d -= 1 left += 1 ans += right - left + 1 return ansn = int(input())s = input().strip()ans = n*(n+1)//2 - f(1)print(ans)C-数学
题意:
给你一个数组以及一个01子串,对于第i位,表示:该位置位颜色红色其数目位ai。可以进行如下操作任意次:选择i,j,使ai+1,aj-1
求最小的操作数,使得两种颜色的平均数相等
题解:
显然的总数是不变的,为了:
由:
显然的:
n = int(input())a = list(map(int, input().split()))s = input().strip()cnt0 = 0cnt1 = 0sum0 = 0sum1 = 0for i in range(n): if s[i] == '0': cnt0 += 1 sum0 += a[i] else: cnt1 += 1 sum1 += a[i]tot = sum0 + sum1if (cnt0 * tot) % n != 0: print(-1)else: print(abs((cnt0*tot)//n) - sum0)D-构造
对于某个值 x,排列 b,c中都只能各出现一次,因此 a 中同一个值最多只能出现两次;若出现超过两次,必然无解。
对于只出现一次的数字,需要决定它在 b 还是 c 中与 a 匹配。目标是让 b 和 c 各自至少有 n/2 个匹配位置。通过变量 need 计算每个排列还需要多少个匹配,然后将前 need 个单次出现的数字分配给 b,其余的分配给c;
若出现两次,就分别放入 b 和 c的不同位置;
若没有出现,收集 b 和 c 各自缺失的数字,按顺序填入未填充的位置。
def main(): n = int(input()) a = list(map(int, input().split())) pos = [[] for _ in range(n + 1)] for i, val in enumerate(a): pos[val].append(i) for x in range(1, n + 1): if len(pos[x]) > 2: print(-1) return b = [0] * n c = [0] * n in_b = [False] * n in_c = [False] * n cnt = 0 for x in range(1, n + 1): if len(pos[x]) == 2: i, j = pos[x] b[i] = x c[j] = x in_b[i] = True in_c[j] = True cnt += 1 sg = [] for x in range(1, n + 1): if len(pos[x]) == 1: sg.append(pos[x][0]) need = n // 2 - cnt for idx in sg[:need]: b[idx] = a[idx] in_b[idx] = True for idx in sg[need:]: c[idx] = a[idx] in_c[idx] = True used_b = set(b) - {0} used_c = set(c) - {0} miss_b = [x for x in range(1, n + 1) if x not in used_b] miss_c = [x for x in range(1, n + 1) if x not in used_c] pb = pc = 0 for i in range(n): if in_b[i]: c[i] = miss_c[pc] pc += 1 else: b[i] = miss_b[pb] pb += 1 print(*b) print(*c)if __name__ == "__main__": main()对于某个值 x,排列 b,c 中都只能各出现一次,因此 a 中同一个值最多只能出现两次;
若出现超过两次,必然无解。
若 x 出现一次,就让对应位置的 bi,ci都等于 x;若出现两次,就分别放入 b 和 c;
若没有出现,则作为缺失值填入 b,c 尚未填的位置。这样 b,c 都是排列,且两个排列中与 a 相等的位置数都不少于 n//2。
def solve() -> None: n = int(input()) a = list(map(int, input().split())) b = [0] * n c = [0] * n p = [[] for _ in range(n + 1)]
for i, val in enumerate(a): p[val].append(i)
ms = [] for x in range(1, n + 1): if len(p[x]) > 2: print(-1) return if not p[x]: ms.append(x) elif len(p[x]) == 1: pos = p[x][0] b[pos] = c[pos] = x else: b[p[x][0]] = x c[p[x][1]] = x
eb = [i for i in range(n) if not b[i]] ec = [i for i in range(n) if not c[i]]
for i in range(len(ms)): b[eb[i]] = ms[i] c[ec[i]] = ms[i]
print(' '.join(map(str, b))) print(' '.join(map(str, c)))
if __name__ == "__main__": solve()E-图论+生成树+DSU
不太会DSU,赛时写的并查集,通过枚举排除一种颜色,检查剩下的边能否构成生成树,以及这棵树是否有两种颜色
错误点在于当单色足以联通时会误判
def find(p, x): root = x while p[root] != root: root = p[root] while x != root: nxt = p[x] p[x] = root x = nxt return rootdef union(p, rank, x, y): rx, ry = find(p, x), find(p, y) if rx == ry: return False if rank[rx] < rank[ry]: p[rx] = ry elif rank[rx] > rank[ry]: p[ry] = rx else: p[ry] = rx rank[rx] += 1 return Trueok = Falsen, m = map(int, input().split())g = []for _ in range(m): u, v, w = map(int, input().split()) g.append((u, v, w))
for mss in (0, 1, 2): p = list(range(n + 1)) rank = [0] * (n + 1) ch = [] use = set() for u, v, w in g: if w == mss: continue if union(p, rank, u, v): ch.append((u, v)) use.add(w) if len(ch) == n - 1 and len(use) == 2: ok = True for u, v in ch: print(u, v) breakif not ok: print(-1)合法生成树恰好使用两种颜色。枚举颜色对 (x,y),只保留这两种颜色的边。如果该颜色对构成的子图连通,并且两种颜色各至少有一条边,那么可以先强制加入一条颜色 x 的边和一条颜色 y 的边,再用 DSU 继续扩展成生成树。因为连通图中的任意森林都可以扩展成生成树。
import sys
def solve() -> None: data = sys.stdin.read().strip().split() if not data: return it = iter(data) n = int(next(it)) m = int(next(it))
edges = [] color_id = [-1, -1, -1] # 记录每种颜色的一条边的索引
for i in range(m): u = int(next(it)) v = int(next(it)) w = int(next(it)) edges.append((u, v, w)) color_id[w] = i # 记录该颜色的一条边的索引
if n < 3: print(-1) return
# 并查集实现 parent = [] rank = []
def init_dsu(n): nonlocal parent, rank parent = list(range(n + 1)) rank = [0] * (n + 1)
def find(x): # 路径压缩 while parent[x] != x: parent[x] = parent[parent[x]] x = parent[x] return x
def union(x, y): rx, ry = find(x), find(y) if rx == ry: return False if rank[rx] < rank[ry]: parent[rx] = ry elif rank[rx] > rank[ry]: parent[ry] = rx else: parent[ry] = rx rank[rx] += 1 return True
# 枚举颜色对 (x, y) for x in range(3): for y in range(x + 1, 3): if color_id[x] == -1 or color_id[y] == -1: continue
init_dsu(n) ans = []
# 强制先加入颜色 x 和 y 的各一条边 for idx in (color_id[x], color_id[y]): u, v, w = edges[idx] if union(u, v): ans.append((u, v))
# 继续用剩余边完成生成树 for u, v, w in edges: if w != x and w != y: continue if union(u, v): ans.append((u, v))
if len(ans) == n - 1: out_lines = [f"{u} {v}" for u, v in ans] sys.stdout.write("\n".join(out_lines)) return
print(-1)
if __name__ == "__main__": solve()F-构造
构造一个长度为n的字符串s,使得其中恰好有k个子串是好串,保证:n−1≤k≤2×(n−2)
当k == n - 1时,构造'ab' + 'c'*(n-2)
否则,构造'a' + 'b'*(k - n + 1) + 'a'+'c'*(2*n-k-3)
for _ in range(int(input())): n,k = map(int,input().split()) if k == n-1: s = 'ab' + 'c'*(n-2) else: # n - 2 - k + (n - 1) s = 'a' + 'b'*(k - n + 1) + 'a'+'c'*(2*n-k-3) print(s)