逆序对 树状数组解法 洛谷P1966

博客讨论了一道洛谷上的算法题目,涉及数组操作和最短转换路径问题。作者首先尝试通过离散化和映射解决,然后通过构建逆序对计算移动次数。博客探讨了如何通过重新排序数组并寻找相邻移动的策略来解决这个问题,最终给出AC代码并分析了思路。
摘要由CSDN通过智能技术生成

洛谷P1966
传送门
这是一道洛谷上的小蓝题,本蒟蒻想到最后一步就崩掉了。
下面就好好聊聊此题的思想:
首先先放AC代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
const int mod=1e8-3;
const int MAXN=1e5+5;
int n;
int d[MAXN],tre[MAXN];
int lowbit(int x)
{return x&-x;}
ll find_tre(int t)
{
	ll kase=0;
	for(int i=t;i>0;i-=lowbit(i))
	{
		kase=(kase%mod+tre[i]%mod)%mod;
	}
	return kase%mod;
}
void ADD_tre(int t)
{
	for(int i=t;i<=n;i+=lowbit(i))
	tre[i]++;
}
struct AC
{
	int v,num;
	friend bool operator < (AC m1,AC m2)
	{
		return m1.v<m2.v;
	}
};
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	AC s1[MAXN],s2[MAXN];
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>s1[i].v;
		s1[i].num=i;
	}
	for(int i=1;i<=n;i++)
	{
		cin>>s2[i].v;
		s2[i].num=i;
	}
	sort(s1+1,s1+1+n);
	sort(s2+1,s2+1+n);
	for(int i=1;i<=n;i++)
	{
		d[s1[i].num]=s2[i].num;
	}
	ll kase=0;
	for(int i=1;i<=n;i++)
	{
	    ADD_tre(d[i]);
		kase=(kase%mod+(i-find_tre(d[i]))%mod)%mod; 
	} 
	cout<<kase%mod<<endl;
	return 0;
 } 

初解此题,我很快想到要使其距离最短,就要保证他们的相对位置相同。
举个例子:

原始数组:
A1 1 3 2 4
A2 4 7 1 3
重新排序:
A1 1 2 3 4
A2 1 3 4 7
离散化:
A1 1 2 3 4
A2 1 2 3 4
原始数组离散化:
A1 1 3 2 4
A2 3 4 1 2

现在我们可以发原始问题转换为:如何移动数字,使得原始数组离散化后 A1[ i ]=A2[ i ] 。
这样想想,仿佛问题可以,或者说有机会直接暴力求解。(事实上,动手打代码就各种WA

所以这时候就要将问题进一步转换。

再将 原始数组离散化 后的数组重新排序
A1 1 2 3 4
A2 3 1 4 2

这里提出一个问题:这样重新排序后的数组会影响转移次数吗?(这个问题我想了很久,一直想搞出一个数学证明来,但因为我是蒟蒻,就无了,所以只能口嗨一下思路)
可以这样考虑:
重新排序的意义实际上是构建了一个A1 和 A2之间的映射,即1 对应 3 ,2 对应 1 … 而此题的根本目的是通过移动使得1 对应 1,2 对应 2,所以只用再排完序之后使A2升序就行,而每次只能相邻之间移动使得此题可以通过找逆序对来计算。

映射大法好!本蒟蒻根本想不到zzzzz

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值