题解 P3054
simonG
·
·
题解
前言
由于蒟蒻不会线段树,或者是树状数组,
暴力法可以得到12分(O(n*n))。
所以我们需要一种非常简单的算法,也就是归并,时间复杂度为O(n*logn)。而且易于理解
详解
-
1,
我们知道,超越次数=(int)圈数之差
求出每一头牛在结束时跑了的圈数。我们假定,圈数在整数的情况下,例如
1,2,3
2-1+3-2+3-1=4
总共4次超越
但是,只是应用于整数的情况下
-
2,
所以我们需要考虑精度的问题,然后标记。最后,剩下的整数部分就是求逆序对(求差)的过程。逆序对减去之前标记的就是答案。
1.3,2.8,3.2
在整数情况下还是4次,但是看下例
int(2.8-1.3)+int(3.2-2.8)+int(3.2-1.3)=1+0+1=2
显然不一样了,只有2次。
-
3,
我们一个一个枚举圈数之间的差,需要两层for遍历,时间复杂度为 O(n*n)。
所以,需要用归并排序,一种非常简便的方法,求出逆序对的个数。
代码
```cpp
#include<bits/stdc++.h>//万能头文件
#define ll long long//不开longlong见祖宗
using namespace std;//命名空间
#define maxn 100003//数据范围
ll n,l,c;
ll V[maxn];//速度
ll d[maxn],a[maxn],f[maxn],s[maxn];
inline ll read() {
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9') {
if(ch=='-')w=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
} //快-读
ll Msort(ll l,ll r) { //归并排序
if(l>=r)return 0;//递归边界
ll ans=0;
int mid=(l+r)>>1;//(l+r)/2
ans+=Msort(l,mid);
ans+=Msort(mid+1,r);
for(int i=l; i<=mid; i++)
a[i]=d[i];
for(int i=mid+1,j=r; j>=mid+1; i++,j--)
a[i]=d[j];
int i=l;
int j=r;//左右边界
for(int k=l; k<=r; k++)
if(a[i]<=a[j])
d[k]=a[i++];
else {
ans+=mid-i+1;//求逆序对
d[k]=a[j--];
}
return ans;//逆序对个数
}
int main(){
n=read(),l=read(),c=read();//读入
for(int i=1; i<=n; i++)
V[i]=read();//读入
sort(V+1,V+n+1);//有序性
ll ans=0,t=0;
for(int i=1; i<=n; i++) {
f[i]=l*V[i]/V[n];
d[i]=l*V[i]%V[n];
}//圈数,个数
for(int i=1; i<=n; i++) {
ans+=(i-1)*f[i]-t;
t=t+f[i];
} //枚举
ans-=Msort(1,n);//减去逆序对个数
printf("%lld",ans);//输出
return 0;
}
```
### 后记
[记录](https://www.luogu.com.cn/record/43805109)
遇到难题时,不一定要考虑非常高级的算法,从最简单的开始,逐步往上推,直至最优解。