洛谷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;
}
代码不是很干净,而且压了行……