奶牛跑步(Treap)
题目描述
有N只奶牛在比赛跑步,他们围绕一个长C的圆形跑道跑L圈。所有奶牛在同一个地方起跑,但是不同奶牛的速度不一样。而比赛在最快的奶牛跑完L圈后结束。
现在FJ想知道有多少次“超车”。一次“超车”定义为:在比赛开始后,到比赛结束为止(包含比赛结束的时刻),一只奶牛x赶超了另一只奶牛y,即x,y出现在同一个位置。
输入格式
第一行有三个整数:N,L,C,
第2到第N+1行:第i+1行有一个整数,代表奶牛i的速度speed_i
输出格式
输出只有一行,包含一个整数,即“超车”的总次数
输入样例
20
100
70
1
输出样例
4
【样例解释】
比赛持续了2个单位时间。期间有4次超车:奶牛2超过了奶牛1和奶牛4,奶牛3超过了奶牛1和奶牛4。
【数据范围】
对于60%的数据,1≤N≤5000
对于100%的数据,1≤N≤100000, 1≤L,C≤25000, 1≤speed_i≤1000000
题解:
60%的数据是n^2的时间复杂度,如下:
首先我们知道:
1.只可能是速度快的追上速度慢的,我们先把奶牛按速度从大到小排序。
2.假如奶牛i跑了a圈,奶牛j跑了b圈,(a>b),那么奶牛i可追上奶牛j(a-b)次
等等,a和b并不一定是整数,如果是整数,我们把奶牛跑的圈数从大到小插入队列,维护一个圈数的前缀和,就可以O(1)求出一个奶牛被追上的圈数。比如我们把前i个奶牛都插入了队列,得到前缀和sum【i】,现在第i+1只奶牛跑了c圈,那么被追上的次数为sum【i】-i*c。
事实总是与愿望相违,假如a-b是实数,则追上的次数要向下取整,那么我们用前缀和维护就会错。所以我们只能用(n^2)的方法去求了。
100%的正解,也运用了前缀和的思想,对于是否要向下取整的问题,我们用treap来解决。假如a的小数部分>=b的小数部分,那么向下取整与不向下取整的结果是一样的,反之则向下取整会比原来少了1。向下取整的影响只来自小数部分,所以对于整数部分我们仍用前缀和维护,小数部分我们开一个treap,每次可O(log n)时间查找小数部分比它大(或少)的个数有多少个,再来更新一下答案,最后把当前的奶牛信息存入treap。此题就此解决。
#include<iostream> #include<fstream> #include<algorithm> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> using namespace std; long long cir[100050]; int n,c,cnt,ge; struct Treap { Treap *l,*r; int fix,siz; long long x; }tree[200050]; Treap *root; long long ans,nows,nowi,l,spe[100050]; bool cmp(int a,int b) { return a>b; } int Qs(Treap *a) { if (a==NULL) return 0; return a->siz; } void Lefturn(Treap *&a) { Treap *b = a->r; a->r = b->l; b->l = a; a->siz = Qs(a->l)+Qs(a->r)+1; b->siz = Qs(b->l)+Qs(b->r)+1; a = b; } void Righturn(Treap *&a) { Treap *b= a->l; a->l = b->r; b->r = a; a->siz = Qs(a->l)+Qs(a->r)+1; b->siz = Qs(b->l)+Qs(b->r)+1; a = b; } Treap *NewNode(long long x) { cnt++; tree[cnt].x = x; tree[cnt].fix = rand(); tree[cnt].siz = 1; tree[cnt].l = NULL; tree[cnt].r = NULL; return tree+cnt; } void Updata(Treap *&a,long long x) { if (!a) { a = NewNode(x); return; } else if (a->x>=x) { Updata(a->l,x); a->siz = Qs(a->l)+Qs(a->r)+1; if (a->l->fix<a->fix) Righturn(a); } else { ge = ge+Qs(a->l)+1; Updata(a->r,x); a->siz = Qs(a->l)+Qs(a->r)+1; if (a->r->fix<a->fix) Lefturn(a); } } int main() { freopen("2242.in","r",stdin); freopen("2242.out","w",stdout); srand(937); scanf("%d%d%d",&n,&l,&c); for (int i=1; i<=n; i++) scanf("%lld",&spe[i]); sort(spe+1,spe+1+n,cmp); cir[1] = l*1000000; for (int i=2; i<=n; i++) cir[i] = (long long)(l*spe[i]*1000000)/spe[1]; Updata(root,((long long)cir[1]%1000000)); ans = 0; nows = ((long long)(cir[1])/1000000); for (int i=2; i<=n; i++) { ge = 0; Updata(root,((long long)cir[i]%1000000)); nowi = ((long long)(cir[i])/1000000); ans = ans+(long long)(nows-nowi*(i-1)-ge); nows = nows+nowi; } printf("%lld\n",ans); return 0; }