P1966 火柴排队
https://www.luogu.com.cn/problem/P1966
其实题目意思就是给你两列数,通过交换相邻两个数的位置,上下两个对应的数会有一个差的平方,假设就叫k吧,每两个对应的数就会有个k,最后要求所有k的总和要最小,问最少需要交换多少次?
分析:
想要最后的和小,那么每一个k都要尽量小(废话)。遵循大对大,小对小的原则(好像叫方差…)。其实我们在乎的不是这个数本身的数值,而是它在数列中的排名。就比如A数列的最大的,对应B数列中最大的,A数列中最小的,对应B数列中最小的…以此类推。所以这就要用到离散化的思想。
思路:
让我们来简单分析一下样例2:
A: 1 3 4 2
然后我们将它按大小编个码:
编码:1# 3# 4# 2#
A : 1 3 4 2
code: 1# 3# 4# 2#
//这里code表示的是排名,
//例如1是最小的排第一位,所以是1#;
//2是第二小的排第二位,所以是2#
然后我们对第二组数动手术
A : 1 3 4 2
code: 1# 3# 4# 2#
//这里code表示的是排名,例如1是最小的排第一位,所以是1#;2是第二小的排第二位,所以是2#
B: 1 7 2 4
下标: 1# 2# 3# 4#
hope: 1# 4# 2# 3#
//下标就是下标(后面求逆序对有用)
//hope是我们希望排成的样子,就是跟上面的1 3 4 2对应起来:(大对大,小对小)
//首先对A数组的1#,我们要拿最小的数去对,B数组中最小的数是B[1]=1,它的下标是1,所以填1#
//再举个例子,比如我们要对A的4#(排行老4,最大的),我们要拿B数组最大的去对,找到B[2]=7
//是最大的,那么记录下它的下标就是2#
//最后我们所hope的下标顺序就是1 4 2 3。
然后我们就要考虑调换的问题。本来B数组的排列方式是1 2 3 4(下标),现在我们所hope的是1 4 2 3,这该怎么算呢?
其实我们不妨反过来想,与其想怎么从1 2 3 4变成 1 4 2 3,不如想怎么从1 4 2 3变成1 2 3 4这就变成了一个逆序对的事,只要求1 4 2 3中的逆序对个数就是调换次数。
逆序对:
所谓逆序对就是“反过来”。例如1 3不是,但3 1就是逆序对,(字面意思)
逆序对的一般解法就是打勾数组+前缀和,反过来统计就行了(但这题可以优化)我先来解释一下常规做法:
求 1 4 2 3的逆序对
0 1 2 3 4
1
//一开始1读了进来,在1那儿打了勾。
//然后从右往左统计1之后的的后缀和,为0,不做动作,继续
0 1 2 3 4
1 1
//读入4 打勾,后缀和0,继续
0 1 2 3 4
1 1 1
// 读入2,打勾,发现2后面的4已经在了,后缀和为1,说明发现了一个逆序对,ans++
0 1 2 3 4
1 1 1 1
//读入3,打勾,发现3后面的 4已经在了,后缀和1,又是一个逆序对,ans++
//最后ans=2,符合题意
优化
以上这道题的整体思路就介绍完了。如果你绕出来了,恭喜你!但是很重要的一点是如果模拟以上思路,你的代码会非常难写!所以这就需要优化。
在离散化的时候进行排序。
// A: 1 3 4 2
//下标 1#, 2#, 3#, 4#
//sort 1 2 3 4
// 下标 1#, 4#, 2#, 3#
// B: 1 7 2 4
//下标 1#, 2#, 3#, 4#
//sort 1 2 4 7
//下标 1# 3# 4# 2#
//存入id 1,4,2,3 一一对应
for(int i=1;i<=n;i++) id[b[i].second]=a[i].second;//second是下标
//大家可以自己走一遍,其实最后存入id的就是1 4 2 3,就是我们要求逆序对的那个东西
然后逆序对也是可以优化的(不叫优化吧)其实就是改变一个顺序,从前往后求逆序对,统计前缀和而已。树状数组可以简单地求逆序对和前缀和。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pair_;
const int MAXN=1e5+5;
const int mod=99999997;
int n;
pair_ a[MAXN],b[MAXN];
int c[MAXN],id[MAXN];
ll ans;
inline int lowbit(int x)
{
return x&(-x);
}
inline void add(int x,int v)
{
while(x<=n)
{
c[x]+=v;
c[x]%=mod;
x+=lowbit(x);
}
}
inline int pre_sum(int x)
{
int res=0;
while(x>=1)
{
res+=c[x];
res%=mod;
x-=lowbit(x);
}
return res;
}
inline bool cmp(pair_ x,pair_ y)
{
return x.first<y.first;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].first;
a[i].second=i;
}
for(int i=1;i<=n;i++)
{
cin>>b[i].first;
b[i].second=i;
}
sort(a+1,a+n+1,cmp);
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;i++) id[b[i].second]=a[i].second;
for(int i=1;i<=n;i++)
{
add(id[i],1);
ans=ans+i-pre_sum(id[i]);
ans%=mod;
}
cout<<ans;
return 0;
}