Algorithm 使用完全k翻转操作生成的长度为n的不同二进制序列数
考虑长度为N的二进制序列b。最初,所有位都设置为0。我们定义了一个具有两个参数的翻转操作,flip(L,R),这样:Algorithm 使用完全k翻转操作生成的长度为n的不同二进制序列数,algorithm,Algorithm,考虑长度为N的二进制序列b。最初,所有位都设置为0。我们定义了一个具有两个参数的翻转操作,flip(L,R),这样: 索引在L和R之间的所有位都被“翻转”,这意味着值为1的位变为值为0的位,反之亦然。更确切地说,对于[L,R]:b[i]=范围内的所有i!b[i] 指定范围之外的位不会发生任何变化 要求您确定使用对任意给定数字进行模翻转操作(我们称之为MOD 更具体地说,每个测试在第一行包含一个数字T,即要给出的查询数。然后是T个查询,每个查询的形式为N,K,MOD,其含义来自上面 一,≤
- 索引在L和R之间的所有位都被“翻转”,这意味着值为1的位变为值为0的位,反之亦然。更确切地说,对于[L,R]:b[i]=范围内的所有i!b[i]
- 指定范围之外的位不会发生任何变化李>
更具体地说,每个测试在第一行包含一个数字T,即要给出的查询数。然后是T个查询,每个查询的形式为N,K,MOD,其含义来自上面
- 一,≤ N、 K≤ 30万
- T≤ 二百五十
- 二,≤ 摩登派青年≤ 10000007
- 测试中所有N-s的总和为≤ 60万
- 时限:2秒
- 内存限制:65536 KB
示例:
输入:
1
211000
输出:
3
说明:
只有一个查询。最初的顺序是00。我们可以执行以下操作:
翻转(1,1)⇒ 10
翻转(2,2)⇒ 01
翻转(1,2)⇒ 11
因此,有3种可能的序列可以使用1次翻转生成
我所做的一些快速观察,尽管我不确定它们是否完全正确:
如果K足够大,也就是说如果我们有足够多的翻转,我们应该能够获得2n个序列。
如果K=1,那么我们寻找的结果是N(N+1)/2。它也是C(n,1)+C(n,2),其中C是二项系数。
目前正在尝试暴力手段,看看我是否能发现某种规则。我认为这是一些二项式系数的总和,但我不确定。
我还遇到了这个问题的一个更简单的变体,其中翻转操作只翻转一个指定的位。在这种情况下,结果是 C(n,k)+C(n,k-2)+C(n,k-4)+……+C(n,(1或0))。当然,这里有一个特殊的情况,k>n,但这并不是很大的区别。无论如何,很容易理解为什么会发生这种情况。我想这是值得注意的。以下是一些想法:
L1
,我们可以只做(L1,L2-1)
,而(R1+1,R2)
翻转。当一段位于另一段内部时,处理方式类似k
段可以获得的不同序列数:C(n+1,2*k)(我们选择段的2*k端。它们总是不同的。左端是排他的)K
flips,答案将是K=0…K的C(n+1,2*K)
K
翻转的序列转换为正好K
翻转的序列(例如,我们可以将同一段翻转两次并添加两个操作。我们还可以将包含两个以上元素的段拆分为两个段并添加一个操作)C(n+1,0)+C(n+1,2)+…+C(n+1,2*K)-d
,其中,如果n=1或K=1,则d=1
,否则0
下面是我用来查找运行蛮力搜索的模式并验证公式是否适用于小n
和k
的代码:
reachable = set()
was = set()
def other(c):
"""
returns '1' if c == '0' and '0' otherwise
"""
return '0' if c == '1' else '1'
def flipped(s, l, r):
"""
Flips the [l, r] segment of the string s and returns the result
"""
res = s[:l]
for i in range(l, r + 1):
res += other(s[i])
res += s[r + 1:]
return res
def go(xs, k):
"""
Exhaustive search. was is used to speed up the search to avoid checking the
same string with the same number of remaining operations twice.
"""
p = (xs, k)
if p in was:
return
was.add(p)
if k == 0:
reachable.add(xs)
return
for l in range(len(xs)):
for r in range(l, len(xs)):
go(flipped(xs, l, r), k - 1)
def calc_naive(n, k):
"""
Counts the number of reachable sequences by running an exhaustive search
"""
xs = '0' * n
global reachable
global was
was = set()
reachable = set()
go(xs, k)
return len(reachable)
def fact(n):
return 1 if n == 0 else n * fact(n - 1)
def cnk(n, k):
if k > n:
return 0
return fact(n) // fact(k) // fact(n - k)
def solve(n, k):
"""
Uses the formula shown above to compute the answer
"""
res = 0
for i in range(k + 1):
res += cnk(n + 1, 2 * i)
if k == 1 or n == 1:
res -= 1
return res
if __name__ == '__main__':
# Checks that the formula gives the right answer for small values of n and k
for n in range(1, 11):
for k in range(1, 11):
assert calc_naive(n, k) == solve(n, k)
此解决方案比穷举搜索要好得多。例如,如果我们使用Pascal三角形计算系数,它可以在每个测试用例的O(N*K)
时间内运行。不幸的是,它不够快。我知道如何更有效地求解素数MOD
(使用卢卡斯定理),但在一般情况下没有解
乘法模逆不能马上解决这个问题,因为k代码>或(n-k)代码>可能没有逆模MOD
注:我假设C(n,m)
是为所有非负n
和m
定义的,如果n
,则等于0
我想我现在知道如何解决任意MOD
的问题了
让我们将MOD
分解为素因子p1^a1*p2^a2*…*pn^an
。现在可以独立地解决每个素因子的这个问题,并使用中国剩余定理组合结果
我们来确定一个素数p。让我们假设p^a | MOD
(也就是说,我们需要得到结果模p^a
)。我们可以预先计算阶乘的所有p
-自由部分,以及将所有0的阶乘除以的p
的最大幂,你已经在一条具有二项式系数的好路径上了。有几个因素需要考虑:
把你的数字想象成一个l的二进制字符串
powers = [0] * (N + 1)
p_free = [i for i in range(N + 1)]
p_free[0] = 1
for cur_p in powers of p <= N:
i = cur_p
while i < N:
powers[i] += 1
p_free[i] /= p
i += cur_p
[0, 1, 0, 0, 1] number
[a, b, c, d, e] number of flips.
[5, 0, 0, 0] mod 2 [1, 0, 0, 0]
[3, 1, 1, 0] [1, 1, 1, 0]
[4, 1, 0, 0] [0, 1, 0, 0]
v = (k % 2 == n % 2) ? n : n - 1
v = k
noOfAvailableFlips:
if k < n:
return k
else:
return (k % 2 == n % 2) ? n : n - 1
flipArrayNo(flippedbits):
return factorial(n) / (factorial(flippedbits) * factorial(n - flippedbits)
solutionsByFlipping(n, k):
res = 0
for i in [k % 2, noOfAvailableFlips(), step=2]:
res += flipArrayNo(i)
return res
# we can append 1 to any arrangement of k non-adjacent blocks of contiguous 1's
# that ends in 1, or to any arrangement of (k-1) non-adjacent blocks of contiguous
# 1's that ends in 0:
a[n][k] = a[n - 1][k] + b[n - 1][k - 1]
# we can append 0 to any arrangement of k non-adjacent blocks of contiguous 1's
# that ends in either 0 or 1:
b[n][k] = b[n - 1][k] + a[n - 1][k]
# complete answer would be sum (a[n][i] + b[n][i]) for i = 0 to k