P3460 [POI2007]TET-Tetris Attack 题解
题意
有一个栈,其中包含 \(2n\) 个元素,有 \(n\) 个标记,每种元素都有一个标记,对于每一种标记,栈中都有 \(2\) 个。你可以交换相邻两个元素的位置。如果两个相邻元素相同,则将他们删除,删除后,他们上面的元素就会掉下来。问将栈清空使用的最少操作次数以及方案。
解题思路
开一个树状数组,用来维护元素间的距离,如果前面有和目前元素标记相同的元素,就移动两个元素直接的距离减一次,过程中顺便记录移动的位置,如果前面没有,就把当前元素的位置记录下来,以便后续处理,最后的答案就是移动的次数,而方案已经在移动的过程中记录下来了。
自行理解一下吧,还是很好懂的。对于一个标记第一次出现的位置和第二次出现的位置,我们没有必要把第一个往前换或把第二个往后换,尽管这样可能引来一连串的消消乐,但我们直接把第一个往后换或把第二个往前换其实也有同样的效果。
代码实现
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, tr[N], ans[N], cnt, vis[N], tot; //cnt用来记录目前已经删除的元素个数,vis数组用来记录前一个与目前元素相同标记的元素的变号
int lowbit(int x) {return x & -x;}
void add(int x, int y) //树状数组模板
{
while (x <= n)
{
tr[x] += y;
x += lowbit(x);
}
}
int query(int x)
{
int res = 0;
while (x)
{
res += tr[x];
x -= lowbit(x);
}
return res;
}
int main()
{
scanf("%d", &n);
n <<= 1;
for (int i = 1; i <= n; i ++)
{
int x;
scanf("%d", &x);
add(i, 1); //i位置上加了1个元素
if (!vis[x]) vis[x] = i;
else
{
int dis = query(i - 1) - query(vis[x]); //dis是元素当前元素与前一个元素之间的距离
for (int j = 1; j <= dis; j ++) ans[++ tot] = i - cnt - j; //路上需要交换的点直接计入答案,注意要减去已经删除的点的个数
add(vis[x], -1), add(i, -1); //消除这两个点
cnt += 2; //删除点数加2
}
}
printf("%d\n", tot);
for (int i = 1; i <= tot; i ++) printf("%d ", ans[i]); //输出
return 0; //完结撒花!
}
祝大家新年快乐!

浙公网安备 33010602011771号