题意:n个数,各出现两次,当两个相同的数相邻时,这两个数同时消除,上方的方块同时下落且可继续消除满足条件的。每次可交换相邻的两个方块,求最少交换次数使得所有数全消除
首先,对于一对相同的数中间有另一对的情况(即两对相同的数是嵌套关系),肯定要先消除另一对
其次,对于一对相同的数中间有另一对中的一个的情况(即两对相同的数有交集),先消除哪个其实是一样的(把一对换到另一对外或把另一对换到这一对里是等价的)
那么我们有两种实现方式:
(1)方法一
用栈模拟我们上述过程,如果一个元素已经出现在栈里,找出并删除同时进行维护,对答案的贡献就是两个位置中间栈的元素数,即 top-i (i为第一次出现的位置在栈中位置)
var
n,x,top,ans :longint;
vis :array[0..50010] of boolean;
z :array[0..100010] of longint;
i :longint;
procedure find(x,pos:longint);
var
i,j:longint;
begin
for i:=top downto 1 do
if z[i]=x then break;
inc(ans,top-i);
for j:=i to top-1 do z[j]:=z[j+1];
dec(top);
end;
begin
read(n);
for i:=1 to 2*n do
begin
read(x);
if not vis[x] then
begin
vis[x]:=true;
inc(top); z[top]:=x;
end else find(x,i);
end;
writeln(ans);
end.
经亲测
(2)方法二
依次读入的时候,如果是第二次出现,那么就把它和第一次出现的位置合并,那么必须交换的次数为(second-first-1)即两者之间的距离,证明见上分类。用树状数组维护即可
var
n,x,ans :longint;
i :longint;
first :array[0..50010] of longint;
t :array[0..100010] of longint;
function lowbit(x:longint):longint;
begin
exit(x and (-x));
end;
procedure add(x,v:longint);
begin
while (x<=n*2) do
begin
inc(t[x],v);
inc(x,lowbit(x));
end;
end;
function find(x:longint):longint;
var
ans:longint;
begin
ans:=0;
while (x>0) do
begin
inc(ans,t[x]);
dec(x,lowbit(x));
end;
exit(ans);
end;
begin
read(n); ans:=0;
for i:=1 to n*2 do
begin
read(x);
if first[x]=0 then
begin
first[x]:=i;
add(i,1);
end else
begin
inc(ans,find(i)-find(first[x]-1)-1);
add(first[x],-1);
end;
end;
writeln(ans);
end.
经亲测
两种方法的思路是一样的,即对答案的贡献为第一次位置和第二次位置之间的距离(中间的满足条件的消除后的),只是维护的方法不一样而已
第一种用到栈顶的距离维护,第二种用树状数组维护
——by Eirlys