[CF复盘] Codeforces Round 874 (Div. 3) 20230520】

总结

  • G好像lc出过,F卡一小时,G不想补了。
  • A模拟
  • B贪心
  • C模拟
  • D贪心+分类讨论
  • E贪心+并查集
  • F逆元+前缀乘在这里插入图片描述

A. Musical Puzzle

在这里插入图片描述

2. 思路分析

3. 代码实现


PROBLEM = """用长度为2的字符串组合出整个串s,要多少种不同的串
"""


#       ms
def solve():
    n, = RI()
    s, = RS()
    p = set()
    for i in range(n - 1):
        p.add(s[i:i + 2])
    print(len(p))

B. Restore the Weather

链接: B. Restore the Weather

1. 题目描述

在这里插入图片描述

2. 思路分析

3. 代码实现

PROBLEM = """输入n、k和长为n的数组a、b。
a[i]代表第i天预测的气温。
b[i]代表第i天实际气温。但是b被打乱了。
已知每天的abs(预测值-实际值)<=k。
请还原出一个正确的b。数据保证有解
"""
"""贪心思考,优先匹配大的或者小的值即可。由于数据保证有解,k实际没用。
为了代码方便,从大的开始匹配,这样b就可以一直pop
"""


#       ms
def solve():
    n, k = RI()
    a = RILST()
    b = RILST()
    b.sort()
    ans = [0] * n
    for i in sorted(range(n), key=lambda x: -a[x]):
        ans[i] = b.pop()
    print(*ans)

C. Vlad Building Beautiful Array

链接: C. Vlad Building Beautiful Array

1. 题目描述

在这里插入图片描述

2. 思路分析

3. 代码实现


PROBLEM = """输入n和n个正整数a[i]。
构造一个长为n的数组b。
其中b[i]要么等于a[i],要么等于a[i]-a[j]。其中j随便选1~n。
要求b中所有数据奇偶性相同。
"""
"""分别尝试奇数或者偶数即可。
这题灵神还挖掘了更多性质,其实代码可以很短,只讨论最小的奇数即可。
但比赛中没必要深挖,直接写即可"""


#       ms
def solve():
    n, = RI()
    a = RILST()
    even, odd = [], []
    for v in a:
        if v & 1:
            odd.append(v)
        else:
            even.append(v)
    if not even or not odd:
        return print("YES")
    even.sort()
    odd.sort()

    def try_1():
        for i, v in enumerate(a):
            if v & 1:
                continue
            if odd[0] >= v:
                return False
        return True

    def try_2():
        for i, v in enumerate(a):
            if not v & 1:
                continue
            if odd[0] >= v:
                return False
        return True

    if try_1() or try_2():
        return print("YES")
    print("NO")

D. Flipper

链接: D. Flipper

1. 题目描述

在这里插入图片描述

2. 思路分析

3. 代码实现



PROBLEM = """给一个长为n的排列p。你必须做如下操作恰好一次:
1. 选一个子段[l,r],(1<=l<=r<=n)翻转这个段。
2. 把这个段两边的数据交换。即交换[1~l-1],[r+1,n],注意这两段可以为空

输出操作后字典序最大的p
"""
"""贪心,枚举l即可。注意讨论
- 先找最大的数即n的位置pos,让pos作为r,枚举l。若n在最后,则可以作为r,直接翻转到0.
- 若n在0上,由于必须翻转一次,n一定会向后, 那么考虑让n-1到0,方法和前边一样。
---
- 这题也有O(n)的做法。可以看看灵神的视频。
找到pos后,前边段其实是不变的,r翻转后也是不变的。讨论r-1即可。
"""



#       ms
def solve():
    n, = RI()
    p = RILST()
    if n == 1:
        return print(1)
    ans = p[::-1]
    pos = p.index(n)
    if pos:
        ans = max(ans, p[pos+1:]+p[pos:] + p[:pos])
        mx = []
        r = pos
        for l in range(pos):
            mx = max(mx, p[l:r][::-1] + p[:l])
        ans = max(ans, p[pos:] + mx)
    else:
        pos = p.index(n - 1)
        ans = max(ans, p[pos+1:]+p[pos:] + p[:pos])
        mx = []
        r = pos
        for l in range(pos):
            mx = max(mx, p[l:r][::-1] + p[:l])
        ans = max(ans, p[pos:] + mx)

    print(*ans)

E. Round Dance

链接: E. Round Dance

1. 题目描述

在这里插入图片描述

2. 思路分析

3. 代码实现


PROBLEM = """给出n和长为n的数组a[i].其中a[i]是i的一个邻居。
已知每个i其实有2邻居,整体组成多个环。
问最少、最多有几个环。
"""
"""并查集。
直接按已知邻居合并。最后有几个家族,最多就有几个环。
最少怎么讨论呢。
    - 已知的边合并时,若x,y已经在一个环里了,要么是x连过y,要么是z连过y,且z和x已经连接。显然第二种情况会导致这个环闭合。
    - 那么这就可以计算一个封闭的环。先计算出有多少个封闭的环。cc
    - 再计算有多少个点在非封闭的家族里。(这里计算有1个即可,因为是讨论最少),把所有非封闭的点连成1个环。op
    - mn=cc+op
"""



class DSU:
    def __init__(self, n):
        self.fathers = list(range(n))
        self.size = [1] * n  # 本家族size
        self.edge_size = [0] * n  # 本家族边数(带自环/重边)
        self.n = n
        self.setCount = n  # 共几个家族

    def find_fa(self, x):
        fs = self.fathers
        t = x
        while fs[x] != x:
            x = fs[x]
        while t != x:
            fs[t], t = x, fs[t]
        return x

    def union(self, x: int, y: int) -> bool:
        x = self.find_fa(x)
        y = self.find_fa(y)

        if x == y:
            # self.edge_size[y] += 1
            return False
        # if self.size[x] > self.size[y]:  # 注意如果要定向合并x->y,需要干掉这个;实际上上边改成find_fa后,按轶合并没必要了,所以可以常关
        #     x, y = y, x
        self.fathers[x] = y
        self.size[y] += self.size[x]
        self.edge_size[y] += 1 + self.edge_size[x]
        self.setCount -= 1
        return True


#       ms
def solve():
    n, = RI()
    a = RILST()
    dsu = DSU(n)

    c = set()  # 环内的点
    vis = set()
    for i, v in enumerate(a):
        if not dsu.union(i, v - 1) and (v - 1, i) not in vis:
            c.add(dsu.find_fa(i))
        vis.add((i, v - 1))

    cc = set()  # 每个环的代表元
    for p in c:
        cc.add(dsu.find_fa(p))
    op = set()  # 不在环的点
    for i in range(n):
        if dsu.find_fa(i) not in cc:
            op.add(i)
            break  # 只要一个就行
    if not op:
        mn = len(cc)
    elif not cc:
        mn = 1
    else:
        mn = len(cc) + 1
    # print(cc,op)
    print(mn, dsu.setCount)

F. Ira and Flamenco

链接: F. Ira and Flamenco

1. 题目描述

在这里插入图片描述

2. 思路分析

3. 代码实现


MOD = 10 ** 9 + 7
PROBLEM = """输入n,m和长为n的数组a[i]。
你需要从中选m个数。其中每两个数都不同,且每两个数的差值严格小于m。
问有多少种方案。下标不同则视为不同方案。
"""
"""逆元/区间乘/乘法原理
看错题很久, 后来发现严格选m个数,且差值小于m,那只能选一段连续的数字。
每个数字有cnt[x]个,那么乘法原理计算即可。剩下的问题就是区间乘怎么弄。
可以滑窗,维护长为m的段,合法情况是段首段尾的值正好差m-1。
每次乘上窗右侧,除去窗左侧出窗的数即可,由于除法不满足同余,因此要乘逆元logn。
但直接预处理前缀乘,就可以O(n)计算每个逆元。
---
赛后卡哈希了,哭了
"""

#       ms
def solve():
    n, m = RI()
    a = RILST()
    cnt = Counter(a)
    b = sorted((k, v) for k, v in cnt.items())
    fact = [1]
    for c,v in b:
        fact.append(fact[-1] * v % MOD)
    inv = [1] * len(fact)
    inv[-1] = pow(fact[-1], MOD - 2, MOD)
    for i in range(len(b) - 1, -1, -1):
        inv[i] = b[i][1] * inv[i + 1] % MOD

    ans = 0
    for i in range(m - 1, len(b)):
        j = i - m + 1
        if j >= 0 and b[j][0] == b[i][0] - m + 1:
            ans = (ans + fact[i + 1] * inv[j]) % MOD

    print(ans % MOD)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值