2021-09-21

洛谷P1966 [NOIP2013 提高组] 火柴排队 题解

题面
下面是题解:
1.转化问题:
首先,先将题干对“距离”的要求进行转化:
思考什么情况下两个相邻的火柴互换可以
令"距离“缩短。我当时感性思考加上理性
验证(其实就是直觉+碰上了),得出
这样一个结论:

距离最小的方案就是让a[i]与b[i] 在各自序列的大小次序(就是第几大)相同。

说白了,就是a[i]第几大,b[i]就第几大
下面给出我的推导(不一定对啊):
(a[i]-b[i])2 +(a[i+1]-b[i+1])2 >(a[i]-b[i+1])2 +(a[i+1]-b[i])2
展开,两边同时削去平方项,再同乘-1/2:
a[i]b[i]+a[i+1]b[i+1]<a[i]b[i+1]+a[i+1]b[i]
移项:
a[i] (b[i+1]-b[i])>a[i+1] (b[i+1]-b[i])
由此,我们可以看出来,b[i+1]>b[i]时,若a[i]<a[i+1]
则互换a[i],a[i+1]可以缩短”距离“。
同理,b[i+1]<b[i]时,若a[i]>a[i+1]
则互换a[i],a[i+1]可以缩短”距离“。
其他同理。于是乎,上面的结论正确。
再明确一下结论:a[i]第几大,b[i]就要第几大

2.发现本质:求逆序对
思考冒泡排序的流程,互换的次数就是逆序对的个数
因为每一次互换恰好消除一个逆序对。所以逆序对
的个数就是互换次数。
所以,这道题等价于求:
b[i]<b[j]且a[i]>a[j]的个数
上面一节的结论告诉我们,这道题要解决二维偏序问题,
而逆序对就是一个二维偏序问题。
(不知道二维偏序没关系,我理解的也不好……)
逆序对求的是,i<j且a[i]>a[j]的对数。
这道题求的是:b[i]<b[j]且a[i]>a[j]的个数
不就是一个问题嘛!
所以,这道题其实就是求逆序对个数的题,只不过
包装的比较好。树状数组还是归并排序都行,搞线段树都没人管你……

完结撒花!

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define il inline
#define re register
il int read()
{
	int s=0,w=1;char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') w=-1;c=getchar();
	}
	while(c>='0'&&c<='9'){
		s=(s<<1)+(s<<3)+c-'0';c=getchar();
	}return s*w;
}
const int N=1e5+10;
const LL mod=1e8-3;
int n,a[N],b[N],c[N],d[N],ct,dt,q[N],toa[N],tob[N];
int TR[N];
LL ans;
struct node{
	int a,b;
}mat[N];
/*
NOTE
0对应a,1对应b 
*/
il bool cmp(node c,node d)
{
	return c.a<d.a;
}
il int lowbit(int x){ return x&(-x); }
il int getsum(int x){
	int res=0;
	while(x>0){
		res+=TR[x];x-=lowbit(x);
	}return res;
}
il void upd(int x)
{
	while(x<=dt){
		TR[x]++;x+=lowbit(x);
	}return;
}
int main()
{
	n=read();
	for(re int i=1;i<=n;i++) c[i]=a[i]=read();
	for(re int i=1;i<=n;i++) d[i]=b[i]=read();
	sort(c+1,c+1+n);sort(d+1,d+1+n); 
	ct=unique(c+1,c+1+n)-c-1;
	dt=unique(d+1,d+1+n)-d-1;
	for(re int i=1;i<=n;i++) a[i]=lower_bound(c+1,c+1+ct,a[i])-c,toa[a[i]]=i;
	for(re int i=1;i<=n;i++) b[i]=lower_bound(d+1,d+1+dt,b[i])-d,tob[b[i]]=i;
	for(re int i=1;i<=n;i++) q[toa[i]]=tob[i];
	for(re int i=1;i<=n;i++){
		ans=(ans+ 1LL*(i-1-getsum(q[i])) )%mod;
		upd(q[i]);
	}
	printf("%lld",(ans%mod+mod)%mod);
	return 0; 
}

代码不是很干净,而且压了行……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值