AtCoder ABC150

文章介绍了Atcoder编程竞赛中的三个问题,涉及Python函数、数学运算(如permutations,gcd,lcm)、整数处理(如去除偶数倍lcm)、异或操作在模式匹配中的应用(KMP),以及巧妙的计数方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C题
签到题。会python的permutations 或者C++的next_permutation就能做。

D题
乍一看很简单,把所有元素折半求最小公倍数lcm,然后求lcm的奇数倍即可。但是有坑:
比如6 4这种情况,lcm=6 但6=61 6=41.5,因此无法满足要求。原因是4折半后为2,导致求得的lcm可以被2整除,如6/2不为半数。去掉这种情况就能AC。

# -*- coding: utf-8 -*-
# @time     : 2023/6/2 13:30
# @file     : atcoder.py
# @software : PyCharm

import bisect
import copy
import sys
from itertools import permutations
from sortedcontainers import SortedList
from collections import defaultdict, Counter, deque
from functools import lru_cache, cmp_to_key
import heapq
import math
sys.setrecursionlimit(200050)


def gcd(x, y):
    if x > y:
        x, y = y, x
    while x:
        x, y = y % x, x
    return y


def main():
    items = sys.version.split()
    if items[0] == '3.10.6':
        fp = open("in.txt")
    else:
        fp = sys.stdin
    n, m = map(int, fp.readline().split())
    a = list(map(int, fp.readline().split()))
    for i in range(n):
        a[i] = a[i] // 2
    gm = a[0]
    for i in range(1, n):
        g = gcd(a[i], gm)
        gm = gm // g * a[i]

    for i in range(n):
        if (gm // a[i]) % 2 == 0:
            print(0)
            return

    ans = m // gm - (m // (2 * gm))
    print(ans)


if __name__ == "__main__":
    main()

E题
下面两个很不错的题。
本题考察如何按位置来计数。首先题目可以把f(S,T)f(S,T)f(S,T)转换为求f(S)=S⊕Tf(S)=S \oplus Tf(S)=ST下按照原规则计算,然后答案乘上2n2^n2n
如 第二个例子中 5 8
可以看到以下222^222组是等价的
(1,0),(0,0)(1,0) ,(0,0)(1,0),(0,0)
(0,0),(1,0)(0,0),(1,0)(0,0),(1,0)
(1,1),(0,1)(1,1),(0,1)(1,1),(0,1)
(1,0),(0,0)(1,0),(0,0)(1,0),(0,0)
现在原题就转换为求s∈0...2ns \in {0...2^n}s0...2nf(s)f(s)f(s)之和。
显然c应该从大到小排序。

如果按照每个c从第一位到第n位来计数,那么不难利用组合数推出一个O(n2)O(n^2)O(n2)算法。但是时间不够。

比较巧妙的计数方法是将每一位的计算拆开来。
假设当前已经到了第i位(i从1开始计数)
例如要求的c数组长度为3,可以列出8个异或值。
1,1,11,0,01,0,11,1,00,0,00,0,10,1,00,1,11,1,1\\1,0,0\\1,0,1\\1,1,0\\0,0,0\\0,0,1\\0,1,0\\0,1,11,1,11,0,01,0,11,1,00,0,00,0,10,1,00,1,1
看第3位,他对答案的贡献是3+2+1+2=8(从上往下)
我们可以将其拆成两部分,第一部分是“只要填了1就可以贡献1”
由于这部分和其他无关,因此贡献值一共为2n−12^{n-1}2n1
第二部分是“前面的某一位填了1因此要多贡献1”
这部分仅仅和前面的那一位相关(当然这一位也要填1),因此贡献值是2n−2∗(i−1)2^{n-2}*(i-1)2n2(i1)
这样答案就已经出来了。
回到样例,例如第一组1,1,11,1,11,1,1中,第一部分贡献1,第二部分分别由前面的数字贡献了1+1,因此是3;第三组1,0,11,0,11,0,1中,第一部分贡献1,第二部分由第一位1贡献了1,因此是2.

# -*- coding: utf-8 -*-
# @time     : 2023/6/2 13:30
# @file     : atcoder.py
# @software : PyCharm

import bisect
import copy
import sys
from itertools import permutations
from sortedcontainers import SortedList
from collections import defaultdict, Counter, deque
from functools import lru_cache, cmp_to_key
import heapq
import math
sys.setrecursionlimit(200050)


def main():
    items = sys.version.split()
    if items[0] == '3.10.6':
        fp = open("in.txt")
    else:
        fp = sys.stdin
    n = int(fp.readline())
    a = list(map(int, fp.readline().split()))
    a.sort(reverse=True)
    mod = 10 ** 9 + 7

    pw = [1] * (n + 1)
    for i in range(1, n + 1):
        pw[i] = pw[i - 1] * 2 % mod

    ans = 0
    for i in range(n):
        ans += (pw[n - 1] + pw[n - 2] * i) * a[i]
    ans = ans * pw[n] % mod
    print(ans)


if __name__ == "__main__":
    main()

F题
看到这种求shift的题,第一感觉一定是将匹配串重复一遍。
好吧,既然说到了模式串和匹配串,那么不得不说到kmp。单纯的kmp似乎没什么思路,但是如果将每个数组相邻的数进行异或操作,就变成了kmp模板题。

# -*- coding: utf-8 -*-
# @time     : 2023/6/2 13:30
# @file     : atcoder.py
# @software : PyCharm

import bisect
import copy
import sys
from itertools import permutations
from sortedcontainers import SortedList
from collections import defaultdict, Counter, deque
from functools import lru_cache, cmp_to_key
import heapq
import math
sys.setrecursionlimit(1000)


def get_next(p):
    p = [0] + list(p)
    m = len(p)
    ne = [0] * m
    i, j = 2, 0
    while i < m:
        while j and p[i] != p[j + 1]:
            j = ne[j]
        if p[i] == p[j + 1]:
            j += 1
        ne[i] = j
        i += 1
    return ne


def kmp_match(s, p, ne):
    s = [0] + list(s)
    p_len = len(p)
    p = [0] + list(p)
    i, j = 1, 0
    n, m = len(s), len(p)
    m_list = []
    while i < n:
        while j and j + 1 < m and s[i] != p[j + 1]:
            j = ne[j]
        if j + 1 < m and s[i] == p[j + 1]:
            j += 1
        if j == p_len:
            bi, ei = i - p_len + 1, i
            j = ne[j]
            m_list.append(bi - 1)
        i += 1
    return m_list


def main():
    items = sys.version.split()
    if items[0] == '3.10.6':
        fp = open("in.txt")
    else:
        fp = sys.stdin
    n = int(fp.readline())
    a = list(map(int, fp.readline().split()))
    b = list(map(int, fp.readline().split()))
    a = a + a
    da, db = [], []
    for i in range(2 * n - 2):
        da.append(a[i + 1] ^ a[i])
    for i in range(n - 1):
        db.append(b[i + 1] ^ b[i])
    ne = get_next(db)
    m_list = kmp_match(da, db, ne)
    for i in m_list:
        print(i, a[i] ^ b[0])


if __name__ == "__main__":
    main()

关于 AtCoder Beginner Contest 387 的信息如下: ### 关于 AtCoder Beginner Contest 387 AtCoder Beginner Contest (ABC) 是一项面向编程爱好者的定期在线竞赛活动。对于 ABC387,该赛事通常会在周末举行,并持续大约100分钟,在此期间参赛者需解决一系列算法挑战问题。 #### 比赛详情 - **举办平台**: AtCoder Online Judge System[^2] - **比赛时间长度**: 大约为1小时40分钟 - **难度级别**: 初学者友好型,适合那些刚开始接触竞争性程序设计的人群参与 - **题目数量**: 一般情况下会提供四到六道不同难度级别的题目供选手解答 #### 题目概览 虽然具体细节可能因官方发布而有所变化,但可以预期的是,这些题目将会覆盖基础的数据结构、字符串处理以及简单图论等方面的知识点。每一道题目的描述都会清晰给出输入输出格式说明及样例测试数据以便理解需求并验证解决方案的有效性。 为了获取最准确的比赛时间和确切的题目列表,请访问 [AtCoder 官方网站](https://atcoder.jp/) 并查看最新的公告板或直接导航至对应编号的具体页面来获得更新的信息。 ```python import requests from bs4 import BeautifulSoup def get_contest_info(contest_id): url = f"https://atcoder.jp/contests/{contest_id}" response = requests.get(url) if response.status_code == 200: soup = BeautifulSoup(response.text, 'html.parser') title_element = soup.find('title') problem_list_elements = soup.select('.panel.panel-default a[href^="/contests/{}/tasks"]'.format(contest_id)) contest_title = title_element.string.strip() if title_element else "Contest Title Not Found" problems = [element['href'].split('/')[-1] for element in problem_list_elements] return { "name": contest_title, "problems": problems } else: raise Exception(f"Failed to fetch data from {url}") abc_387_details = get_contest_info("abc387") print(abc_387_details) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值