Codeforces ICPC那场
在许多题目中,我原来感觉第二题应该是不难的,结果难的我都不想做了,所以发一下第二题的题解。
题目链接 : 点击这里
题目的意思便是对一个列表,任意选择范围内的索引i ,使A[i] -= 2,A[(i+1)%len(A)] += 1,我的第一想法便是用差分,使差分列表全部为零即可,但是操作就变得无规律了
为什么我会想到用差分呢,因为改变后的列表都为[n]*(len(A)),其中的n我们并不确定,可能是出现次数最多的元素,也可能是其他元素,但对于任意一个正确答案,只有其差分数组是有规律的,都为【0】乘len(A).
但最后我错了,先给大家看一下正确的代码:
import sys
inp = lambda: sys.stdin.readline().rstrip()
Inp = lambda: [*map(int, sys.stdin.readline().split())]
from bisect import bisect_left, bisect_right
from math import gcd
for _ in range(int(inp())):
n, = Inp()
A = Inp()
C = [0]*n
i = 1
for _ in range(30):
for j in range(n):
if A[j]&i:
C[j] += i
A[j] += i
A[(j-1)%n] -= 2*i
i <<= 1
if -min(C) <= min(A) == max(A):
print(sum(C)-min(C)*n)
else:
print(-1)、
接下来解释一下代码:我们先把列表中的每个数字看成二进制数字,我们要把每个二进制变为一样的,有个结论:在二进制数字中,每次操作都可以看成用高一位的1来换去本位的1,逆向思维,也就相当于用本位的1来换取高一位的1 ,而且大家应该发现了,对每次操作i位置上的1是,操作A[(j-1)%n]这个数字是对这个数字的i位置上的1是没有影响的,即使A[(j-1)%n]数中的i+1位置上为0,可以用更高位置上的1分解来替换的。
二进制操作数中第i位是可以进行i次操作的。
-> 为什么要取30位的二进制呢?
因为30位的二进制数字可以表示2^30−1=1073741824−1=1073741823,而数据范围是1e9,完全是可以包含所有数字的
->这道题只要最后可以把A变为全是元素0的列表,就可以把列表变为元素相同的列表,因为当元素全为k时,我们可以通过len(A)次操作变为元素全部是元素是k-1的,那么我们直接让所有元素全部通过操作可以变为全是0的的话,设答案为元素全是k的列表,通过len(A)*k此操作变为全是0的,鉴于不知道k的具体值为多少,我们只要取一个每个数变为0的可操作次数中最小值,即所有数都可以通过这个操作,那么这个值就是最后的k值**
->为什么次数大的一定会经历与次数小的一样的变化过程呢?
就比如举个例子:数字i通过5次操作变为0,数字j通过3次操作变为0,那么数字i可以通过5-3=2次操作变为j,原理就是这样的。