[acwing周赛复盘] 第 110 场周赛20230701
总结
- 状态不对,把自己写懵了。
- T1 模拟币
- T2 对向双指针/两数之和。
- T3 树状数组优化dp。
5044. 求和
链接: 5044. 求和
1. 题目描述
2. 思路分析
- 模拟。
3. 代码实现
# Problem: 求和
# Contest: AcWing
# URL: https://www.acwing.com/problem/content/5047/
# Memory Limit: 256 MB
# Time Limit: 1000 ms
import sys
import random
from types import GeneratorType
import bisect
import io, os
from bisect import *
from collections import *
from contextlib import redirect_stdout
from itertools import *
from array import *
from functools import lru_cache, reduce
from heapq import *
from math import sqrt, gcd, inf
if sys.version >= '3.8': # ACW没有comb
from math import comb
RI = lambda: map(int, sys.stdin.buffer.readline().split())
RS = lambda: map(bytes.decode, sys.stdin.buffer.readline().strip().split())
RILST = lambda: list(RI())
DEBUG = lambda *x: sys.stderr.write(f'{str(x)}\n')
# print = lambda d: sys.stdout.write(str(d) + "\n") # 打开可以快写,但是无法使用print(*ans,sep=' ')这种语法,需要print(' '.join(map(str, p))),确实会快。
DIRS = [(0, 1), (1, 0), (0, -1), (-1, 0)] # 右下左上
DIRS8 = [(0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1), (-1, 0),
(-1, 1)] # →↘↓↙←↖↑↗
RANDOM = random.randrange(2**62)
MOD = 10**9 + 7
# MOD = 998244353
PROBLEM = """
"""
def lower_bound(lo: int, hi: int, key):
"""由于3.10才能用key参数,因此自己实现一个。
:param lo: 二分的左边界(闭区间)
:param hi: 二分的右边界(闭区间)
:param key: key(mid)判断当前枚举的mid是否应该划分到右半部分。
:return: 右半部分第一个位置。若不存在True则返回hi+1。
虽然实现是开区间写法,但为了思考简单,接口以[左闭,右闭]方式放出。
"""
lo -= 1 # 开区间(lo,hi)
hi += 1
while lo + 1 < hi: # 区间不为空
mid = (lo + hi) >> 1 # py不担心溢出,实测py自己不会优化除2,手动写右移
if key(mid): # is_right则右边界向里移动,目标区间剩余(lo,mid)
hi = mid
else: # is_left则左边界向里移动,剩余(mid,hi)
lo = mid
return hi
def bootstrap(f, stack=[]):
def wrappedfunc(*args, **kwargs):
if stack:
return f(*args, **kwargs)
else:
to = f(*args, **kwargs)
while True:
if type(to) is GeneratorType:
stack.append(to)
to = next(to)
else:
stack.pop()
if not stack:
break
to = stack[-1].send(to)
return to
return wrappedfunc
# ms
def solve():
n,s = RI()
a = RILST()
if sum(a) == s:
print('YES')
else:
print('NO')
if __name__ == '__main__':
t = 1
if t:
t, = RI()
for _ in range(t):
solve()
else:
solve()
5045. 三角形数
链接: 5045. 三角形数
1. 题目描述
2. 思路分析
- 题意转化一下:有一个数列数字均为n(n+1)//2,求从中找两个数加起来等于x。
- 那么显然就是两数之和的双指针思路,从左右两边同时指针寻找,和大就动右指针,和小动左指针。
3. 代码实现
def solve():
n, = RI()
l, r = 1, int((2 * n) ** 0.5)
while l <= r:
x = l * (l + 1) // 2 + r * (r + 1) // 2
if x == n:
return print('YES')
elif x < n:
l += 1
else:
r -= 1
print('NO')
5046. 智商药
链接: 5046. 智商药
1. 题目描述
2. 思路分析
- 手玩一下发现,首先数据里必须有l0和rn,否则没有方案数。不过实现时到时不用管这个条件。
- 仔细想一下,对于一种药(l,r)来讲,吃它只能到达状态r,需要从[l,r-1]这个连续状态转移而来。这里边所有数字都应该是前边一种药的r。
- 那么按照r排序,状态就满足无后效性了,发现每个状态的前驱状态都是连续的,可以用区间求和方法来优化。比如树状数组。
- 用树状数组来当dp数组。
- 令dp[i]为吃到第i种药的方案数,那么dp[i] = sum(dp[x,y]),其中x,y为对应右端点在[l,r-1]区间的药的下标。
3. 代码实现
class BinIndexTree:
""" PURQ的最经典树状数组,每个基础操作的复杂度都是logn;如果需要查询每个位置的元素,可以打开self.a """
def __init__(self, size_or_nums): # 树状数组,下标需要从1开始
# 如果size 是数字,那就设置size和空数据;如果size是数组,那就是a
if isinstance(size_or_nums, int):
self.size = size_or_nums
self.c = [0 for _ in range(self.size + 5)]
# self.a = [0 for _ in range(self.size + 5)]
else:
self.size = len(size_or_nums)
# self.a = [0 for _ in range(self.size + 5)]
self.c = [0 for _ in range(self.size + 5)]
for i, v in enumerate(size_or_nums):
self.add_point(i + 1, v)
def add_point(self, i, v): # 单点增加,下标从1开始
# self.a[i] += v
while i <= self.size:
self.c[i] += v
self.c[i] %= MOD
i += i & -i
# def set_point(self, i, v): # 单点修改,下标从1开始 需要先计算差值,然后调用add
# self.add_point(i, v - self.a[i])
# self.a[i] = v
def sum_interval(self, l, r): # 区间求和,下标从1开始,计算闭区间[l,r]上的和
return (self.sum_prefix(r) - self.sum_prefix(l - 1)) % MOD
def sum_prefix(self, i): # 前缀求和,下标从1开始
s = 0
while i >= 1:
s += self.c[i]
s %= MOD
# i -= i&-i
i &= i - 1
return s
def min_right(self, i):
"""寻找[i,size]闭区间上第一个正数(不为0的数),注意i是1-indexed。若没有返回size+1;复杂度O(lgnlgn)"""
p = self.sum_prefix(i)
if i == 1:
if p > 0:
return i
else:
if p > self.sum_prefix(i - 1):
return i
l, r = i, self.size + 1
while l + 1 < r:
mid = (l + r) >> 1
if self.sum_prefix(mid) > p:
r = mid
else:
l = mid
return r
def kth(self, s):
"""返回<=s的最小下标"""
pos = 0
for j in range(18, -1, -1):
if pos + (1 << j) <= self.size and self.c[pos + (1 << j)] <= s:
pos += (1 << j)
s -= self.c[pos]
return pos
def lowbit(self, x):
return x & -x
# ms
def solve():
n, m = RI()
a = []
for _ in range(m):
a.append(RILST())
a.sort(key=lambda x: x[1])
b = [y for _, y in a]
f = BinIndexTree(m)
ans = 0
for i, (l, r) in enumerate(a, start=1):
x = bisect_left(b, l) + 1
y = bisect_right(b, r - 1) - 1 + 1
if x <= y:
f.add_point(i, f.sum_interval(x, y))
if l == 0:
f.add_point(i, 1)
if r == n:
ans = (ans + f.sum_interval(i, i)) % MOD
print(ans)
六、参考链接
- 无