牛客周赛145-图论/生成树/DSU+构造

1768 字
9 分钟
牛客周赛145-图论/生成树/DSU+构造

牛客周赛145-补EF#

https://ac.nowcoder.com/acm/contest/134981

Easy:A、B、C

Mid:D

Hard:E、F

A-Counter

Counter计数即可

counter
from collections import Counter
s = 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 defaultdict
def 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 ans
n = int(input())
s = input().strip()
ans = n*(n+1)//2 - f(1)
print(ans)
C-数学

题意:

给你一个数组以及一个01子串,对于第i位,表示:该位置位颜色红色其数目位ai。可以进行如下操作任意次:选择i,j,使ai+1,aj-1

求最小的操作数,使得两种颜色的平均数相等

题解:

显然的总数是不变的,为了:

sum0/cnt0=sum1/cnt1sum0×cnt1=sum1×cnt0sum0 / cnt0 = sum1 / cnt1 \\ sum0 × cnt1 = sum1 × cnt0

sum1=totsum0cnt1=ncnt0sum1 = tot - sum0,cnt1 = n - cnt0

sum0×(ncnt0)=(totsum0)×cnt0sum0×nsum0×cnt0=tot×cnt0sum0×cnt0sum0×n=tot×cnt0sum0 × (n - cnt0) = (tot - sum0) × cnt0 \\sum0 × n - sum0 × cnt0 = tot × cnt0 - sum0 × cnt0 \\sum0 × n = tot × cnt0

显然的:

sum0=(cnt0×tot)/nsum0 = (cnt0 × tot) / n

数学
n = int(input())
a = list(map(int, input().split()))
s = input().strip()
cnt0 = 0
cnt1 = 0
sum0 = 0
sum1 = 0
for i in range(n):
if s[i] == '0':
cnt0 += 1
sum0 += a[i]
else:
cnt1 += 1
sum1 += a[i]
tot = sum0 + sum1
if (cnt0 * tot) % n != 0:
print(-1)
else:
print(abs((cnt0*tot)//n) - sum0)
D-构造

对于某个值 x,排列 b,c中都只能各出现一次,因此 a 中同一个值最多只能出现两次;若出现超过两次,必然无解。

对于只出现一次的数字,需要决定它在 b 还是 c 中与 a 匹配。目标是让 bc 各自至少有 n/2 个匹配位置。通过变量 need 计算每个排列还需要多少个匹配,然后将前 need 个单次出现的数字分配给 b,其余的分配给c

若出现两次,就分别放入 bc的不同位置;

若没有出现,收集 bc 各自缺失的数字,按顺序填入未填充的位置​。

构造1
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;若出现两次,就分别放入 bc

若没有出现,则作为缺失值填入 b,c 尚未填的位置。这样 b,c 都是排列,且两个排列中与 a 相等的位置数都不少于 n//2​

构造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 root
def 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 True
ok = False
n, 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)
break
if 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)
牛客周赛145-图论/生成树/DSU+构造
http://blog.7a7a68.xyz/posts/牛客周赛145-补ef/
作者
Waning
发布于
2026-05-24
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
Waning
愿你明日如绚丽之花.
公告
还活着.
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
56
分类
8
标签
20
总字数
125,693
运行时长
0
最后活动
0 天前

目录