题目链接:https://csacademy.com/contest/arhiva/#task/swap_pairing/
大意是给2*n个包含n种数字,每种数字出现恰好2次的数列,每一步操作可以交换相邻的两个数字,问最少需要操作多少次,可以使得所有的同种数字都相邻。
我的做法是考虑不同的数对的数字在原来数列中的位置关系,有三大类,如果我们用[]和{}表示的话就是:
[]{}
[{]}
[{}]
这三种位置情况。
第一种情况对答案的贡献应当是0。
第二种情况对答案的贡献应当是1。
第三种情况对答案的贡献应当是2。
接下来问题就是如何统计。我的方法是先计算所有的pair对,
总共n种数字,那么数对的个数就是(n-1)*n/2。
接下来需要减去第一种情况的个数,再加上第三种情况的个数。
第一种情况的统计应当是比较简单的,第三种情况可以规约到求某个线段内部有多少线段的问题。
统计可以用BIT来维护。
代码如下:

1 #include <iostream> 2 #include <vector> 3 #include <algorithm> 4 #include <string> 5 #include <string.h> 6 #include <stdio.h> 7 #include <math.h> 8 #include <queue> 9 #include <stack> 10 #include <map> 11 #include <ctime> 12 #include <set> 13 using namespace std; 14 15 const int N=123456; 16 int a[N]; 17 map<int,int> le; 18 map<int,int> ri; 19 20 pair<int,int> pos[N]; 21 int v[N]; 22 int lowbit(int x) { 23 return x & -x; 24 } 25 int get(int x) { 26 int ret=0; 27 while (x) { 28 ret+=v[x]; 29 x-=lowbit(x); 30 } 31 return ret; 32 } 33 void add(int x,int add) { 34 while (x<N) { 35 v[x]+=add; 36 x+=lowbit(x); 37 } 38 } 39 int main () { 40 int n; 41 while (scanf("%d",&n)!=EOF) { 42 memset(v,0, sizeof(v)); 43 le.clear(); 44 ri.clear(); 45 for (int i=1;i<=n;i++) { 46 scanf("%d",a+i); 47 if (le[a[i]]==0) 48 le[a[i]]=i; 49 else ri[a[i]]=i; 50 } 51 int cnt=1; 52 for (map<int,int>::iterator it=le.begin();it!=le.end();it++) { 53 int key=it->first; 54 int val1=it->second; 55 int val2=ri[key]; 56 pos[cnt].first=val1; 57 pos[cnt++].second=val2; 58 } 59 sort(pos+1,pos+cnt); 60 int tot=n/2; 61 long long ret=(tot-1)*1LL*tot/2LL; 62 for (int i=tot;i>=1;i--) { 63 int l=pos[i].first; 64 int r=pos[i].second; 65 ret+=get(r); 66 add(r,1); 67 } 68 memset(v,0,sizeof v); 69 for (int i=1;i<=tot;i++) { 70 int l=pos[i].first; 71 int r=pos[i].second; 72 ret-=get(l); 73 add(r,1); 74 } 75 cout<<ret<<endl; 76 77 } 78 }
题解给了另一种做法,可以有一种最优解第一个数字不用移动,则接下来所有数字怎么移动都被固定了,中间仍然是用BIT来维护。