[NOIP2013][逆序对]火柴排队

题目: 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度。 现在将每盒中的火柴各自排成一列, 同一列火柴的高度互不相同,
两列火柴之间的距离定义为: ∑(ai-bi)^2

其中 ai 表示第一列火柴中第 i 个火柴的高度,bi 表示第二列火柴中第 i 个火柴的高度。

每列火柴中相邻两根火柴的位置都可以交换,请你通过交换使得两列火柴之间的距离最小。请问得到这个最小的距离,最少需要交换多少次?如果这个数字太大,请输出这个最小交换次数对
99,999,997 取模的结果。

分析:
我们观察距离的定义:
在这里插入图片描述
所以我们要求sigma(a[i]*b[i])的最大值
显然让a中最大的乘以b中最大的可以得到最大值,而我们同时交换两个序列中的数字等价于一个序列不动,交换另一个序列中的数字

那么我们就可以构造出一个数组c,c[i]代表a序列不动,b序列中的第i个数字要移动到位置c[i]上,此时c中的逆序对个数就是我们的最小交换次数

可以用归并排序或者树状数组求

代码(树状数组):

#include
#define ll long long
using namespace std;
#define mod 99999997
long long tr[100010];
const int N=100005;
int n;
int bb[N];
void add(int k,ll num)
{
    while(k<=n)
    {
        tr[k]=(tr[k]%mod+num%mod)%mod;
        k+=k&-k;
    }
}
int read(int k)
{
    ll sum=0;
    while(k)
    {
        sum=(sum%mod+tr[k]%mod)%mod;
        k-=k&-k;
    }
    return sum;
}
struct node
{
    int val,pos;
}aa[100010];
bool cmp(node a,node b) {return a.val < b.val;}
struct premu1
{
	long long val;
	int num;
}a[N];
struct premu2
{
	int num;
	long long val;
	int tree;
}b[N];
bool cmpa(premu1 x,premu1 y) {return x.val<y.val;}
bool cmpb(premu2 x,premu2 y) {return x.val<y.val;}
bool cmpc(premu2 x,premu2 y) {return x.num<y.num;}
int sum[N];
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i].val);
		a[i].num=i;
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&b[i].val);
		b[i].num=i;
	}
	sort(a+1,a+n+1,cmpa);
	sort(b+1,b+n+1,cmpb);
	for(int i=1;i<=n;i++) b[i].tree=a[i].num;
	sort(b+1,b+n+1,cmpc);
	for(int i=1;i<=n;i++) sum[i]=b[i].tree;
	
	memset(tr,0,sizeof(tr));
    for(int i=1;i<=n;i++)
    {
           aa[i].val = sum[i];
           aa[i].pos = i;
    }
    sort(aa+1,aa+1+n,cmp);
    int cnt = 1;
    for(int i=1;i<=n;i++)
    {
        if(i != 1 && aa[i].val != aa[i-1].val)
            cnt++;
        bb[aa[i].pos] = cnt;
    }
    ll summ = 0;
    for(int i=1;i<=n;i++)
    {
        add(bb[i],1);
        summ=(summ+(i - read(bb[i]))%(ll)99999997)%(ll)99999997;
    }
    printf("%lld\n",summ);
	return 0;
}

你可能感兴趣的:([NOIP2013][逆序对]火柴排队)