1 条题解
-
0
1. 题目推断
从代码中可以看到:
- 输入
n,然后有2*n个数a[1..2n]。 - 每个数
a[i]在1..n范围内(因为lst[a[i]]数组大小为n)。 - 每个数恰好出现两次(因为
lst[a[i]]非零时表示之前出现过一次)。
这是一个经典的 括号匹配 或 消除游戏 问题。
题目描述推测(根据代码逻辑还原):
有 2n 个数,1 到 n 每个数恰好出现两次。
每次可以消除两个相同的数,但这两个数之间的所有数必须已经被消除。
换句话说,消除是一个合法的括号匹配过程:把第一次出现看作左括号,第二次出现看作右括号,只有当中间的括号全部匹配完毕,才能匹配这对括号。
问:按照某种顺序消除所有数,输出每次消除时,这对数之间还剩多少个未被消除的数(即这对括号内部的、还未匹配的括号数量)。
2. 代码逻辑分析
变量解释
lst[x]:记录数x第一次出现的位置。bit[]:树状数组,记录到当前位置为止已经“删除”了多少个数(这里删除指已经匹配过)。s[]:未使用(可能原代码删除了部分)。ans[]:存储每次匹配时,这对数之间还剩下的未匹配数的数量。top:未使用(可能原代码删除了栈部分)。t:已匹配的对数。
流程
for i = 1 to 2n: if lst[a[i]] == 0: 记录第一次出现 lst[a[i]] = i else: // 第二次出现,进行匹配 real_i = i - ask(i); // 在删除一些数后,当前数在剩余序列中的新位置 real_p = lst[a[i]] - ask(lst[a[i]]);// 第一次出现的位置在剩余序列中的新位置 v = real_i - real_p - 1; // 它们之间还剩下多少个数未被匹配 for j = 1 to v: ans[++tot] = real_p + j - 1; // 记录这些“中间剩余数”的编号(从0开始计数) // 将这对数标记为已删除 add(lst[a[i]] + 1, 1); add(i, 1); t++;
3. 详细例子推导
假设输入:
n = 3 a = 1 2 1 3 2 3每个数出现两次。
初始:
lst全 0,bit全 0。i=1: a=1
第一次出现,
lst[1]=1。i=2: a=2
第一次出现,
lst[2]=2。i=3: a=1
第二次出现,匹配
lst[1]=1。real_i = 3 - ask(3) = 3 - 0 = 3real_p = 1 - ask(1) = 1 - 0 = 1v = 3 - 1 - 1 = 1- 循环
j=1 to 1:ans[1]=1+1-1=1(表示这对1之间还剩下 1 个数未被匹配) - 删除这对:
add(2,1)和add(3,1)
树状数组表示位置 2 和 3 各有一个删除标记(前缀和表示到此位置已删的数量)。
i=4: a=3
第一次出现,
lst[3]=4。i=5: a=2
第二次出现,匹配
lst[2]=2。real_i = 5 - ask(5)现在ask(5)= 已删除标记数(位置2和3)在 1..5 中的数量 = 2。 所以real_i = 5-2=3real_p = 2 - ask(2)ask(2)= 位置2有删除标记吗?位置2的删除标记是从add(2,1)来的,ask(2)得到 1。 所以real_p = 2-1=1v = 3 - 1 - 1 = 1- 循环:
ans[2]=1+1-1=1 - 删除这对:
add(3,1)和add(5,1)现在树状数组:位置2=1,位置3=2,位置5=1。
i=6: a=3
第二次出现,匹配
lst[3]=4。real_i = 6 - ask(6)ask(6)= 删除位置2,3,5 的数量 = 1+2+1 前缀和?我们重新算: 位置2:1,位置3:1(来自第一次删除)+1(来自第二次删除)=2,位置5:1。ask(6)= bit[4] + bit[6] ?
更准确:ask(6)= 已删除的数量 = 第一次删除两个数(位置2,3) + 第二次删除两个数(位置3,5)?不对,第二次删除是add(3,1)和add(5,1),所以位置3加了两次(总共删了3个数?) 我们直接模拟树状数组: 初始全0。 第一次删除:add(2,1),add(3,1) 第二次删除:add(3,1),add(5,1) 所以: 位置1:0 位置2:1 位置3:2 位置4:0 位置5:1 位置6:0ask(6)= sum[1..6] = 0+1+2+0+1+0? 不对,树状数组前缀和是 bit 数组的某个组合。 实际计算: ask(6): 6(110): bit[6]=bit[4]+bit[6]? 我们直接手算: 树状数组维护的是单点更新,前缀和查询。 更新: add(2,1): bit[2]=1, bit[4]=1, bit[8...] add(3,1): bit[3]=1, bit[4]=2, bit[8...] add(3,1): bit[3]=2, bit[4]=3 add(5,1): bit[5]=1, bit[6]=1, bit[8...] 所以: bit[1]=0, bit[2]=1, bit[3]=2, bit[4]=3, bit[5]=1, bit[6]=1 ask(6) = bit[6]+bit[4] = 1+3=4。 所以real_i = 6-4=2。real_p = 4 - ask(4)ask(4) = bit[4]=3,所以real_p=4-3=1。v = 2-1-1=0- 循环不执行(因为 v=0)
- 删除这对:
add(5,1)和add(6,1)
最终 ans = [1,1],输出:
2 1 1
4. 答案含义
ans中存储的是:每次匹配时,这两个匹配数之间还剩下多少个未被匹配的数(在原序列中的相对位置编号,从0开始)。题目要求输出这些剩余数的个数,并按照匹配发生的顺序输出。
5. 算法总结
- 遍历序列,第一次出现的数记录位置。
- 第二次出现时,计算在原序列中,这两个位置之间还有多少个数未被匹配(通过树状数组维护已匹配删除的数量,得到“压缩后”的位置差)。
- 输出这些中间剩余数的编号(从0开始的索引)。
- 更新树状数组标记这对数已匹配。
6. 时间复杂度
- 遍历:O(n)
- 树状数组操作:O(log n)
- 总复杂度:O(n log n)
- 输入
- 1
信息
- ID
- 5175
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者