考虑长度 N 的二进制序列 b 。最初,所有位都设置为0.我们使用2个参数定义一个翻转操作,翻转(L,R),这样:
要求您确定可以使用完全 K 以任意给定数字为模的翻转操作获得的可能不同序列的数量,让我们称之为的 MOD 即可。
更具体地说,每个测试在第一行包含一个数字 T ,即要给出的查询数。然后有T个查询,每个查询的形式为 N , K , MOD ,具有上述含义。
示例:
输入:
1
2 1 1000
输出:
3
说明:
只有一个查询。初始序列为00.我们可以执行以下操作:
翻转(1,1)⇒10
翻转(2,2)⇒01
翻转(1,2)⇒11
因此,有3个可能的序列可以使用恰好1个翻转生成。
我做了一些快速观察,虽然我不确定它们是完全正确的:
如果K足够大,那就是如果我们有足够多的翻转,我们应该能够获得2个 n 序列。
如果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> 0。 ñ,但这并不是一个巨大的差异。无论如何,它很容易理解为什么会发生这种情况。我想这值得注意。
答案 0 :(得分:3)
以下是一些想法:
我们可以假设没有两次翻转操作(否则,我们可以假设它没有发生)。它确实影响了操作次数,但我稍后会谈到它。
我们可以假设没有两个段相交。实际上,如果L1 < L2 < R1 < R2
,我们可以执行(L1, L2 - 1)
和(R1 + 1, R2)
翻转。一个段在另一个段内的情况类似地处理。
我们也可以假设没有两个片段相互接触。否则,我们可以将它们粘合在一起并减少操作次数。
这些观察结果给出了以下公式:通过精确翻转k
段,可以获得不同序列的数量,而没有&#34;冗余&#34;翻转:C(n + 1,2 * k)(我们选择2 * k个段的末尾。它们总是不同的。左端是独占的)。
如果我们执行的动作不超过K
次,则答案为sum for k = 0...K of C(n + 1, 2 * k)
直观地说,似乎可以将不超过K
翻转的序列转换为精确K
次翻转的序列(例如,我们可以翻转同一段的两个以上)时间并添加2个操作。我们还可以将两个以上元素的一个段拆分为两个段并添加一个操作)。
通过执行强力搜索(我知道它不是真正的证据,但看起来与上面提到的观察相结合),如果n或k等于1,确切地说就是总和。
也就是说,结果为C(n + 1, 0) + C(n + 1, 2) + ... + C(n + 1, 2 * K) - d
,d = 1
如果n = 1 or k = 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
(使用Lucas&#39;定理),但O在一般情况下没有解决方案。
乘法模块化反转无法立即解决此问题,因为k!
或(n - k)!
可能没有反模MOD
。
注意:我假设所有非负C(n, m)
和n
都定义了m
,如果0
则等于n < m
。
我想我现在知道如何为任意MOD
解决它。
让我们将MOD
分解为素因子p1^a1 * p2^a2 * ... * pn^an
。现在可以独立地解决每个素数因子的这个问题,并使用中国剩余定理来组合结果。
让我们来修一个素数。我们假设p^a|MOD
(也就是说,我们需要以模p^a
的形式得到结果)。我们可以使用以下内容预先计算所有p
- 免费的阶乘部分和p
的最大幂,它将线性时间内的所有0 <= n <= N
的阶乘除以:
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
现在,p-free
部分的因子是所有p_free[i]
的{{1}}的乘积,而除i <= n
的p的幂是n!
的前缀和1}}。
现在我们可以划分两个阶乘:powers
- 免费部分与p
是互质的,所以它总是有一个倒数。 p^a
的权力只是被减去。
我们几乎就在那里。还有一点观察:我们可以在线性时间内预先计算p
- 自由部分的逆。让我们使用欧几里德算法计算p
的无p部分的逆。现在,我们可以将所有N!
从i
迭代到0. N
的倒数 - p
的自由部分是i!
次{{}的倒数1}}(如果我们使用i + 1
元素与p_free[i]
相互作用的元素在乘法中形成一个阿贝尔群的事实,我们将p无效部分的逆转换为产品很容易证明。
此算法在每个测试用例的p^a
时间内运行。现在它看起来不错了。
答案 1 :(得分:1)
你已经拥有了二项式系数的好路径。有几个因素需要考虑:
将您的号码视为长度为n
的二进制字符串。现在我们可以创建另一个数组,计算一个位被翻转的次数:
[0, 1, 0, 0, 1] number
[a, b, c, d, e] number of flips.
但偶数次翻转都会导致相同的结果,所有奇数翻转都是如此。所以基本上分布的相关部分可以表示为%2
逻辑上的下一个问题:有多少种偶数和奇数值的组合可用。我们稍后会处理这个顺序,现在只是假设为了简单起见,将翻转数组按顺序递减。我们从k
开始,作为数组中唯一的翻转数字。现在我们要添加一个翻盖。由于整个翻转数组使用%2,我们需要从k
的值中删除两个来实现这一点,并将它们分别插入到数组中。 E.g:
[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]
正如最后一个例子所示(记住我们在最终结果中操作模2),移动单个1并不会改变最终结果中的翻转次数。因此,我们总是必须翻转翻转数组中的偶数位。如果k
是偶数,则翻转位的数量也是相同的,反之亦然,无论n
的值是多少。
所以现在的问题当然是有多少种不同的阵列填充方式?为简单起见,我们马上从mod 2开始
显然,如果k
是奇数,我们从1个翻转位开始,否则用1开始。我们总是添加2个翻转位。我们可以继续这样做,直到我们要么翻转所有n
位(或者至少翻转我们可以翻转的位数)
v = (k % 2 == n % 2) ? n : n - 1
或者我们无法在数组上进一步传播k
。
v = k
把它们放在一起:
noOfAvailableFlips:
if k < n:
return k
else:
return (k % 2 == n % 2) ? n : n - 1
到目前为止,总有v / 2
翻转数组(mod 2)与翻转位数有所不同。现在我们来到下一部分来置换这些数组。这只是一个简单的排列函数(排列,重复精确):
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
这也表明,对于足够大的数字,我们无法获得2 ^ n个序列,原因很简单,我们无法按照自己的意愿安排操作。实际影响结果的翻转次数将始终为偶数或奇数,具体取决于k
。没有办法解决这个问题。可以得到的最好结果是2^(n-1)
序列。
答案 2 :(得分:0)
为了完整起见,这是一个动态程序。它可以很容易地处理任意模数,因为它是基于总和的,但不幸的是我还没有找到一种方法来加速O(n * k)
。
令a[n][k]
为长度n
的二进制字符串数,其k
个非邻近的1
个1
块以b[n][k]
结尾。设n
为k
长度1
的二进制字符串数,0
以# 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
结尾的a[n][k]
个非相邻块。
然后:
b[n][k]
我想知道以下观察是否有用:(1)n < 2*k - 1
和k
在(n + 1) / 2
时为零,而(2)在另一方面为{{{{1} 1}}大于⌊a = [[0] * 11 for i in range(0,11)]
b = [([1] + [0] * 10) for i in range(0,11)]
def f(n,k):
return fa(n,k) + fb(n,k)
def fa(n,k):
global a
if a[n][k] or n == 0 or k == 0:
return a[n][k]
elif n == 2*k - 1:
a[n][k] = 1
return 1
else:
a[n][k] = fb(n-1,k-1) + fa(n-1,k)
return a[n][k]
def fb(n,k):
global b
if b[n][k] or n == 0 or n == 2*k - 1:
return b[n][k]
else:
b[n][k] = fb(n-1,k) + fa(n-1,k)
return b[n][k]
def g(n,k):
return sum([f(n,i) for i in range(0,k+1)])
# example
print(g(10,10))
for i in range(0,11):
print(a[i])
print()
for i in range(0,11):
print(b[i])
⌋整体答案似乎相同。
Python代码(完整矩阵是为了简单起见而定义的,但我认为实际上只需要一行,在空间方面,对于自下而上的方法):
#define VERTEX_BUFFER_BIND_ID 0
....
vertices.inputAttributes[0].binding = VERTEX_BUFFER_BIND_ID;
vertices.inputAttributes[0].location = 0;
vertices.inputAttributes[0].format = VK_FORMAT_R32G32B32_SFLOAT;
vertices.inputAttributes[0].offset = offsetof(Vertex, position);
// Attribute location 1: Color
vertices.inputAttributes[1].binding = VERTEX_BUFFER_BIND_ID;
vertices.inputAttributes[1].location = 1;
vertices.inputAttributes[1].format = VK_FORMAT_R32G32B32_SFLOAT;
vertices.inputAttributes[1].offset = offsetof(Vertex, color);