这道题比较有思维含量。
用到树状数组
难 度 : 提 高 + / 省 选 − {\color{Blue}{难度:提高+/省选-}} 难度:提高+/省选−
题目大意:
给出两个数列 { a i } \{a_i\} {
ai}和 { b i } \{b_i\} {
bi},长度均为 n n n。一次操作可以使其中一个数列的一组数交换位置。现在给出 n n n和这两个数列,请你求出最小的交换次数 k k k,使得下面这个式子:
∑ i = 1 n ( a i − b i ) 2 \sum^{n}_{i=1}(a_i-b_i)^2 i=1∑n(ai−bi)2
值最小。
思路:
这道题大概想了将近 30 mins 30\operatorname{mins} 30mins。考虑以下两个引理:
-
引 理 1 : 引理1: 引理1:
为 了 使 调 动 次 数 k 最 小 , 那 么 只 调 动 一 个 数 列 中 元 素 的 方 案 , 一 定 更 优 或 等 价 于 调 动 其 中 两 个 数 列 中 元 素 的 方 案 。 为了使调动次数k最小,那么只调动一个数列中元素的方案,\\一定更优或等价于调动其中两个数列中元素的方案。 为了使调动次数k最小,那么只调动一个数列中元素的方案,一定更优或等价于调动其中两个数列中元素的方案。 -
引 理 2 : 引理2: 引理2:
当 两 个 数 列 都 升 序 或 降 序 排 列 时 , 上 式 的 值 最 小 。 当两个数列都升序或降序排列时,上式的值最小。 当两个数列都升序或降序排列时,上式的值最小。
引理1显而易见,对于第二个引理,我们可以用排序不等式证明(时间不够了,这个坑待填)。
考虑到数据范围,先对两个数列进行离散化,再以 a [ i ] a[i] a[i]为关键字对 { b i } \{b_i\} { bi}中的元素求逆序对的个数,即为本题答案。
别忘了取模
A C C o d e : {\color{Red}{AC\space Code:}} AC Code:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int MOD=1e8-3;
int n;
int a[maxn],b[maxn],c[maxn],d[maxn];
int q[maxn],tree[maxn];
inline int lowbit(int x)
{
return x&-x;
}
inline void add(int x,int y)
{
for(;x<=n;x+=lowbit(x)) tree[x]+=y,tree[x]%=MOD;
}
inline int ask(int x)
{
int ans=0;
for(;x;x-=lowbit(x)) ans+=tree[x],ans%=MOD;
return ans;
}
inline bool cmp1(int i,int j)
{
return a[i]<a[j];
}
inline bool cmp2(int i,int j)
{
return b[i]<b[j];
}
inline void work()
{
int ans=0;
for(int i=1;i<=n;i++)
{
add(q[i],1);
ans+=i-ask(q[i]);
ans%=MOD;
}
cout<<ans<<endl;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
c[i]=i;
}
for(int i=1;i<=n;i++)
{
cin>>b[i];
d[i]=i;
}
sort(c+1,c+n+1,cmp1);
sort(d+1,d+n+1,cmp2);
for(int i=1;i<=n;i++) q[c[i]]=d[i];
work();
}