P1966 [NOIP2013 提高组] 火柴排队【题解】

这道题比较有思维含量。

用到树状数组

难 度 : 提 高 + / 省 选 − {\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=1n(aibi)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(); 
}

从 不 灌 水 , 坚 持 每 篇 博 文 精 雕 细 琢 。 从不灌水,坚持每篇博文精雕细琢。

猜你喜欢

转载自blog.csdn.net/qq_62444770/article/details/120679894