http://www.spoj.com/problems/CTRICK/这是一个关于spoj的问题 它说明了
魔术师将一小包卡片洗牌,将其面朝下放置并执行以下步骤:
顶部卡片移动到包装的底部。新的顶级卡面朝上放在桌子上。这是黑桃王牌。
从顶部到底部一次移动两张卡片。下一张牌面朝上放在桌子上。这是黑桃之二。
一次移动一张卡片......
这种情况一直持续到第n张和最后一张卡片成为黑桃的n。
如果魔术师知道如何预先安排牌(并知道如何进行虚假洗牌),这个令人印象深刻的技巧就有效。您的程序必须确定给定数量的卡的卡的初始顺序,1≤n≤20000。
输入
在输入的第一行是一个正整数,告诉我要遵循的测试用例数。每个案例由一行包含整数n组成。 输出
对于每个测试用例,输出一行,其值为1到n的正确排列,空格分隔。第一个数字显示包装的顶部卡片等... 实施例
输入: 2
4
5
输出:
2 1 4 3
3 1 4 5 2
现在我能想到的唯一解决方案是使用队列并模拟流程。 但那将是O(n ^ 2)。我读了评论,他们建议使用BIT的分段树。 我知道段树和BIT,但我无法理解如何在这个问题中实现它们。请建议一些方法来做到这一点。 感谢名单
答案 0 :(得分:3)
我不知道为什么这个问题应该与BIT或分段树相关联,但我使用简单的“O(N ^ 2)”模拟解决了这个问题。
首先,此问题的时间限制为11s
和N == 20000
。这表明O(kN)
解决方案可以解决问题。我相信你认为这个k
应该是N
,因为简单的模拟需要这个,但不知何故它可以被优化。
让我们看看在N == 5时如何构造序列:
Round 1, count 1 space starting from first space after last position: _ 1 _ _ _
Round 2, count 2 spaces starting from first space after last position: _ 1 _ _ 2
Round 3, count 3 spaces starting from first space after last position: 3 1 _ _ 2
Round 4, count 4 spaces starting from first space after last position: 3 1 4 _ 2
Round 5, count 5 spaces starting from first space after last position: 3 1 4 5 2
我们可以看到一个很好的模式:对于圆形i
,我们应该从最后一个位置后的第一个空格开始计算i
空格,并在必要时变回。
然而,关键的一步是:在几轮之后,剩下的空间将小于要计算的空间。在这种情况下,我们可以使用mod
来节省时间!
例如,在前一个示例的第4轮中,我们只剩下2个空格但需要计算4个空格。如果算上4,那就浪费时间了。 计数4个步骤相当于从最后一个位置后的第一个空格开始计算4%2 == 0个空格。您可以自己验证这一点:)
因此,我们可以使用代码模拟此过程:
memset(ans, 255, sizeof(ans));
while (cur <= n)
{
int i, cnt;
int left = n - cur + 1; // count how many spaces left
left = cur % left + 1; // this line is critical, mod to save time!
for (i = pos, cnt = 0; ; ++i) // simulate the process
{
if (i > n) i = 1;
if (ans[i] == -1) ++cnt;
if (cnt == left) break;
}
ans[i] = cur;
pos = i;
++cur;
}
答案 1 :(得分:2)
如果你想使用Fenwick树(BIT)来解决这个问题,请仔细看看nevets发布的解决方案,特别是这部分(感谢绘图):
Round 1, count 1 space starting from first space after last position: _ 1 _ _ _
Round 2, count 2 spaces starting from first space after last position: _ 1 _ _ 2
Round 3, count 3 spaces starting from first space after last position: 3 1 _ _ 2
Round 4, count 4 spaces starting from first space after last position: 3 1 4 _ 2
Round 5, count 5 spaces starting from first space after last position: 3 1 4 5 2
使用上述方法找到正确的自由空间时间复杂度为O(N),因为我们必须通过所有空间(总复杂度O(N ^ 2))。请注意,我们可以使用以下方法计算下一个位置:
free(next_pos) = (free(current_pos) + next_number) mod free(total) + 1
其中free(x)告诉我们有多少空闲空间到(包括)一个位置。这不是next_pos的直接公式,但它告诉我们它需要满足什么,所以我们可以使用这些信息进行二进制搜索。
唯一要做的就是进行自由空间计算,这就是BIT发挥作用的地方,因为它为查询和更新提供了O(log N)的时间复杂度。找到空闲空间的时间复杂度现在为O(log ^ 2 N),总时间复杂度为O(N log ^ 2 N)。
至于跑步速度:
我必须说我对速度增益感到非常惊讶: - )
P.S。如果您不确定如何使用BIT,请通过将所有值更新+1来初始化。在标记一个插槽时,只需将其更新为-1即可。