记录了初步解题思路 以及本地实现代码;并不一定为最优 也希望大家能一起探讨 一起进步
目录
-
-
- [10/12 2897. 对数组执行操作使平方和最大](#10/12 2897. 对数组执行操作使平方和最大)
- [10/13 3727. 最大交替平方和](#10/13 3727. 最大交替平方和)
- [10/14 3723. 数位平方和的最大值](#10/14 3723. 数位平方和的最大值)
- [10/15 2913. 子数组不同元素数目的平方和 I](#10/15 2913. 子数组不同元素数目的平方和 I)
- [10/16 2916. 子数组不同元素数目的平方和 II](#10/16 2916. 子数组不同元素数目的平方和 II)
- [10/17 3551. 数位和排序需要的最小交换次数](#10/17 3551. 数位和排序需要的最小交换次数)
- 10/18
-
10/12 2897. 对数组执行操作使平方和最大
AND/OR 操作不会增加或减少某一位上 1 的总数,只会把某个数的 1 挪到另一个数上。
平方和要最大,应该把 1 尽量集中到少数几个大数上,因为把一位从小数挪到大数,平方和会变大。
先统计每一位上有多少个 1,再贪心拼出 k 个数:每次把还没用完的位都放进当前这个数,让它尽量大,然后把平方加进答案。
python
def maxSum(nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: int
"""
mod = 10**9 + 7
cnt = [0] * 31
for x in nums:
for i in range(31):
if x >> i & 1:
cnt[i] += 1
ans = 0
for _ in range(k):
x = 0
for i in range(31):
if cnt[i]:
x |= 1 << i
cnt[i] -= 1
ans = (ans + x * x) % mod
return ans
10/13 3727. 最大交替平方和
偶数位取正、奇数位取负,平方只和绝对值有关。
将绝对值大的放到偶数位,绝对值小的放到奇数位。
先取绝对值排序,较小的一半减去平方,较大的一半加上平方。
python
def maxAlternatingSum(nums):
"""
:type nums: List[int]
:rtype: int
"""
ans=0
n=len(nums)
l = [abs(x) for x in nums]
l.sort()
for i in range(n):
if i<n//2:
ans-=l[i]**2
else:
ans+=l[i]**2
return ans
10/14 3723. 数位平方和的最大值
要凑恰好 num 位、数位和恰好是 sum 的数,让各位数字的平方和最大;平方和一样时取数值最大的那个。
平方和固定时数字越集中越大,所以尽量多用 9,剩下不足 9 的部分单独放一位。
同分时要数值最大,就把这些 9 放在最高位,余数紧跟其后,剩下的位补 0。
如果 sum 比 9*num 还大,凑不出来,返回空串。
python
def maxSumOfSquares(num, sum):
"""
:type num: int
:type sum: int
:rtype: str
"""
if num * 9 < sum:
return ""
k, s = divmod(sum, 9)
ans = "9" * k
if s:
ans += str(s)
return ans + "0" * (num - len(ans))
10/15 2913. 子数组不同元素数目的平方和 I
每个子数组都要算一次「里面有多少个不同的数」,再把这个数平方后全部加起来。
数组很短,枚举左端点,再从左往右把右端点一格一格延长。
用集合记下当前这段出现过的数,集合大小就是不同元素个数,平方后加进答案。
python
def sumCounts(nums):
"""
:type nums: List[int]
:rtype: int
"""
n = len(nums)
ans = 0
for i in range(n):
seen = set()
for j in range(i, n):
seen.add(nums[j])
ans += len(seen) * len(seen)
return ans
10/16 2916. 子数组不同元素数目的平方和 II
还是把每个子数组的不同元素个数平方后加起来,但数组很长,不能一段段枚举。
固定右端点从左往右扫,记下每个左端点到当前右端点这段里有多少个不同的数。
新来的数只对「上次出现位置之后」的那些左端点是新元素,这些段的不同个数都加 1。
一段数每个都加 1 时,平方和增加 2×原来的和 + 这段长度。用线段树做区间加 1 并维护平方和,每扫完一个右端点就加进答案,最后对 10^9+7 取模。
python
def sumCounts(nums):
"""
:type nums: List[int]
:rtype: int
"""
mod = 10**9 + 7
n = len(nums)
sm = [0] * (4 * n)
sq = [0] * (4 * n)
lz = [0] * (4 * n)
def apply(p, l, r, v):
length = r - l + 1
sq[p] = (sq[p] + 2 * v * sm[p] + v * v * length) % mod
sm[p] = (sm[p] + v * length) % mod
lz[p] += v
def push(p, l, r):
if lz[p] and l < r:
m = (l + r) // 2
apply(p * 2, l, m, lz[p])
apply(p * 2 + 1, m + 1, r, lz[p])
lz[p] = 0
def update(p, l, r, ql, qr):
if ql <= l and r <= qr:
apply(p, l, r, 1)
return
push(p, l, r)
m = (l + r) // 2
if ql <= m:
update(p * 2, l, m, ql, qr)
if qr > m:
update(p * 2 + 1, m + 1, r, ql, qr)
sm[p] = (sm[p * 2] + sm[p * 2 + 1]) % mod
sq[p] = (sq[p * 2] + sq[p * 2 + 1]) % mod
last = {}
ans = 0
for r, x in enumerate(nums):
update(1, 0, n - 1, last.get(x, -1) + 1, r)
last[x] = r
ans = (ans + sq[1]) % mod
return ans
10/17 3551. 数位和排序需要的最小交换次数
数位和更小的排前面,数位和相同则数字更小的排前面。
先按这个规则排出目标顺序,再看每个数现在在哪、应该去哪。
这些「当前位置指向目标位置」会连成若干个圈。一个圈里有 k 个数,最少交换 k-1 次就能各归其位,总次数就是 n 减去圈的个数。
python
def minSwaps(nums):
"""
:type nums: List[int]
:rtype: int
"""
def digit_sum(x):
s = 0
while x:
s += x % 10
x //= 10
return s
n = len(nums)
order = sorted((digit_sum(x), x) for x in nums)
pos = {x: i for i, (_, x) in enumerate(order)}
vis = [False] * n
ans = n
for i in range(n):
if vis[i]:
continue
ans -= 1
j = i
while not vis[j]:
vis[j] = True
j = pos[nums[j]]
return ans
10/18
python