luogu P1966 火柴排队 树状数组 逆序对 离散化
pass
我们直观上就希望ai和bi尽可能相似。我们做出一个大胆的猜想,是不是a序列中排名第i的,和b序列中排名第i的放在一个位置是最优的呢。多试几组数据发现是这样的
题目中让我们求交换次数。而我们现在也只关心相对排名,所以原先的数值不重要了。我们把原先的两个序列离散化一下,都变成1-n。
那现在就变成了求,两个1-n的数列,需要交换多少次,才能变成一样的序列。又有一个看起来很对的结论,我们把一个序列保持不动,让另外一个序列向其靠拢,和两个一起变换,在最优情况策略下,次数是一样的。
那把一个序列变成另外一个序列,最优需要交换几次呢?我们联想到逆序对,是假设有x个逆序对,我们只要交换x次,就能把一个序列变成有序。那么我们如果把另外一个序列当有有序的标准,去求另一个序列的逆序对不就是答案么。
用归并排序,在以一个序列为标准的情况下,做逆序对是不方便的。我们有另一种求逆序对的方法,树状数组求逆序对。从前往后扫描序列,当前为x,看树状数组中,[x,maxn]有多少个数。然后把x的位置+1。那这个题也是一样的,只不过外面嵌套一个loc[]而已,从而用另一个序列的标准去求逆序对。因为树状数组的原因,要颠倒一下。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int MAXN = 110000,mo = 99999997; 5 int n,a[MAXN],b[MAXN],tp[MAXN],loc[MAXN],tre[MAXN],ans; 6 int lowbit(int x) 7 { 8 return x & -x; 9 } 10 int query(int x) 11 { 12 int res = 0; 13 for(;x;x -= lowbit(x)) 14 { 15 res += tre[x]; 16 res %= mo; 17 } 18 return res; 19 } 20 void add(int x) 21 { 22 for(;x <= n;x += lowbit(x)) 23 tre[x]++; 24 } 25 int main() 26 { 27 scanf("%d",&n); 28 for (int i = 1;i <= n;i++) 29 { 30 scanf("%d",&a[i]); 31 tp[i] = a[i]; 32 } 33 sort(tp + 1,tp + n + 1); 34 for (int i = 1;i <= n;i++) 35 a[i] = lower_bound(tp + 1,tp + n + 1,a[i]) - tp; 36 for (int i = 1;i <= n;i++) 37 { 38 scanf("%d",&b[i]); 39 tp[i] = b[i]; 40 } 41 sort(tp + 1,tp + n + 1); 42 for (int i = 1;i <= n;i++) 43 { 44 b[i] = lower_bound(tp + 1,tp + n + 1,b[i]) - tp; 45 loc[b[i]] = i; 46 } 47 for (int i = 1;i <= n;i++) 48 { 49 ans += query(n - loc[a[i]] + 1); 50 ans %= mo; 51 add(n - loc[a[i]] + 1); 52 } 53 printf("%d\n",ans); 54 return 0; 55 }
心之所动 且就随缘去吧