题意
给定一个长度为2n的序列,1~n各出现两次,可以交换相邻两项,两个同样的数放在一起会对消,求把所有数对消的最小交换次数,并输出方案。
特性
一种很显然的贪心,两两间原始距离最近的先合并,再到远的。这么做是为了在包含关系的时候先处理掉里面的。
如果要是交叉的话,先删哪个都没所谓,可以自己YY一下。
心路
所以我就想排序后用树状数组维护编号,其它模拟就好,因为总步数才1 000 000,用并查集跳过删除的数字。
结果又T又W(大部分还是对的),不知道为什么。
题解
贪心+树状数组
其实排序可以省掉,只要按顺序每到两个都出现的时候移动到它们消掉就可以了。因为按顺序的时候肯定先把区间小的先访问到,这样省了个O(N logN)的时间。
树状数组不变,还有,因为在把一个数字移到相对应的数字旁边时,很明显路径是连续的,所以可以用一个for来填写。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=50010,MAXC=1000010;//debug 看错 MAXC!=2*MAXN
int n,m,size=0,pos[MAXN];
int s[MAXN*2];
void add(int x,int c)
{
for(;x<=m;x+=x&-x) s[x]+=c;
}
int getsum(int x)
{
int re=0;
for(;x>=1;x-=x&-x) re+=s[x];
return re;
}
int tot=0,list[MAXC];
int main()
{
scanf("%d",&n);m=2*n;
for(int i=1;i<=m;i++)
{
int x;scanf("%d",&x);
if(!pos[x]) pos[x]=i,add(i,1);
else
{
add(pos[x],-1);int res=getsum(i)-getsum(pos[x]-1);//求出两点之间非空的位置有多少个
for(int p=i-size-1;res;res--,p--) list[++tot]=p;//填写方案数组
size+=2;
}
}
printf("%d\n",tot);
for(int i=1;i<=tot;i++) printf("%d\n",list[i]);
return 0;
}