P1966 [NOIP2013 提高组] 火柴排队
原题链接
考察:树状数组+hash
本题的总结:
- 如果b可以交换任意元素到a,那么最少交换次数 = 环长度-1
- 如果b需要交换相邻的到a,那么最少交换次数 = 逆序对数目.
思路:
这道题如果贪心把每个数列的数字尽可能换成一样的是错误的.例二就是HACK数据.既然这样我们就考虑怎么转化答案式子来求最值.
本题求\(\sum\)(ai2-bi2)的最小值即是求\(\sum\)ai2+bi2-2*ai*bi的最小值.因为每个i都会枚举到,所以前面的\(\sum\)(ai2+bi2)是一个定值.我们能做的就是最大化2*ai*bi.
参考下面遗忘的高中数学知识:
所以我们要让离散后的a数组与b数组相等.
那么怎么快速求出交换次数呢?
再以普通的求逆序对的方法求解即可.
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long LL;
const int N = 100010,Mod = 99999997;
int n,a[N],b[N],c[N],tr[N];
vector<int> A;
int get(int x)
{
return lower_bound(A.begin(),A.end(),x)-A.begin()+1;
}
int lowbit(int x)
{
return x&-x;
}
int ask(int x)
{
int ans = 0;
for(int i=x;i;i-=lowbit(i)) ans+=tr[i];
return ans;
}
void add(int k,int x)
{
for(int i=k;i<=n;i+=lowbit(i)) tr[i]+=x;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
A.push_back(a[i]);
}
sort(A.begin(),A.end());
for(int i=1;i<=n;i++)
{
a[i] = get(a[i]);
c[a[i]] = i;
}
A.clear();
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
A.push_back(b[i]);
}
sort(A.begin(),A.end());
for(int i=1;i<=n;i++)
{
int x = get(b[i]);
b[i] = c[x];//求b的逆序对数量
}
int ans = 0;
for(int i=1;i<=n;i++)
{
ans+=ask(n)-ask(b[i]);
ans%=Mod;
add(b[i],1);
}
printf("%d\n",ans);
return 0;
}