做题笔记:洛谷P2448

传送门

前置芝士

前缀和,离散化,树状数组。

正文

首先显然要将位置变动的数离散化

然后考虑将答案分成两部分

1.位置变动过的数彼此之间有多少对逆序对

2.位置变动过的数与没变动过的数之间有多少对逆序对

对于第一个问题可以直接在离散化后用树状数组解决

第二个问题需要思考:

不妨考虑 \(i\) 号位置在交换结束后里面的数由 \(i\) 变成了 \(x\)

\(i>x\) :

则说明 \(x\)\(i\) 的前面经过那么多交换操作后到了 \(i\) 这里

\(x\)\(i\) 之间有 \(k\) 个位置没变动过的数,显然ans+=k,

因为 \(x\)\(i\) 中的数都大于 \(x\) ,所以每一对逆序对是

左端为 \(x\)\(i\) 中任意一个位置没变动过的数,右端是 \(x\) ,共 \(k\)

\(i<x\) :

则说明 \(x\)\(i\) 的后面经过那么多交换操作后到了 \(i\) 这里

显然还是ans+=k,这次是因为 \(i\)\(x\)中的数都小于x,

所以这时每一对逆序对是左端是 \(x\) ,右端为 \(i\)

\(x\) 中任意一个位置没变动过的数,总共还是 \(k\)

综上,只要求出交换后的序列,对于每一个位置都有:

ans+= 原位置上的数与现位置上的数之间的未变动元素个数

对于求任意一段区间内的未变动元素个数,可以用前缀和

最后输出即可。

AC code

//writer:Oier_szc

#include <bits/stdc++.h>
#define int long long
using namespace std;
//const int N=;
int k,ans=0;
int a[200005],all[200005],ys[200005],tot;
//离散化用的 
int d[200005];
//交换后位置 
int tr[200005];
//树状数组 
int qzh[200005];
//前缀和 
int lowbit(int x)
{
	return x&(-x);
}
void update(int u,int x)
{
	for(int i=u;i<=tot;i+=lowbit(i)) tr[i]+=x;
}
int query(int u)
{
	int res=0;
	for(int i=u;i!=0;i-=lowbit(i))
	{
		res+=tr[i];
	}
	return res;
}
//上面三个函数为树状数组基本操作 
void lsh()
{
	sort(all+1,all+1+2*k);
	tot=unique(all+1,all+1+2*k)-all-1;
	for(int i=1;i<=2*k;++i)
	{
		ys[i]=lower_bound(all+1,all+1+tot,a[i])-all;
	}
}
//离散化 
void work()
{
	for(int i=1;i<=tot;++i)
	{
		d[i]=i;
	}
	for(int i=1;i<=k;++i)
	{
		swap(d[ys[i]],d[ys[i+k]]);
	}
}
//求交换后的序列 
void get_ans()
{
	for(int i=tot;i>=1;--i)
	{
		ans+=query(d[i]-1);
		update(d[i],1);
	}
	//求解问题一 
	for(int i=1;i<=tot;++i)
	{
		qzh[i]=qzh[i-1]+all[i]-all[i-1]-1;
	}
	//对未变动过的数前缀和 
	for(int i=1;i<=tot;++i)
	{
		if(d[i]>i) ans+=qzh[d[i]]-qzh[i];
		else ans+=qzh[i]-qzh[d[i]];
	}
	//求解问题二 
}
signed main()
{
	scanf("%lld",&k);
	int x,y;
	for(int i=1;i<=k;++i)
	{
		scanf("%lld%lld",&x,&y);
		a[i]=x,a[i+k]=y;
		all[i]=x,all[i+k]=y;
		//这里用a[1~k]表示x,a[k+1~2k]表示y 
	}
	lsh();
	work();
	get_ans();
	printf("%lld\n",ans);
	return 0;
}
posted @ 2023-03-07 14:08  Oier_szc  阅读(67)  评论(0)    收藏  举报