POJ2274[The Race]题解

来源:互联网 发布:linux 窗口编程 编辑:程序博客网 时间:2024/06/06 00:59

题目概述

给出n(n<=250000)辆赛车,i赛车初始在xi,速度为vi,赛车在比赛时会发生超车(不会相撞)。求超车次数%1000000,并输出前10000次(不足10000次有几次输出几次)超车,格式为x y,表示x超过y,时间靠前的先输出,时间相同位置靠前的先输出,保证没有时间相同位置也相同的超车。
示意图

解题报告

第一问显然就是逆序对,用二路归并求解即可。关键就是这个第二问难,好像除了O(n^2)的扫描没有办法。

如果总体来看,非常难处理,我们要把总体的问题尽量缩小,才能方便的处理:对于超车,肯定是相邻两个先超车(超车完之后相邻位置就改了)。有了这个思路,我们可以刚开始相邻算出超车时间,然后从中挑出一个小的,这就是第一次超车,超车完毕之后,因为相邻位置交换,所以要重新对左右两个车进行计算(利用链表就可以快速找到相邻位置),算出新的超车时间,然后又挑出一个小的,重新计算……

每次挑出小的,让我们想到了堆,至此,这道题就解完了。下面给出示意图:

示例程序

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=250000,tt=1000000;struct Car{    int x,v;    bool operator <= (const Car &a) const {return v<=a.v;}}; //赛车的结构体struct Heap{    int x,y;    double ti,dis;    bool operator < (const Heap &a) const {return ti<a.ti||ti==a.ti&&dis<a.dis;}}; //堆的结构体int n,len_s,ans,l[maxn+5],r[maxn+5]; //l[i]表示i的左边,r[i]表示i的右边Car a[maxn+5],b[maxn+5],c[maxn+5];Heap heap_s[maxn+10000+5]; //因为最多做10000次,每次最多会增加一个,所以要开maxn+10000bool Eoln(char ch) {return ch==10||ch==13||ch==EOF;}int readi(int &x) //读入优化{    int tot=0,f=1;    char ch=getchar();if (ch==EOF) return EOF;    while ('9'<ch||ch<'0') {if (ch=='-') f=-f;ch=getchar();}    while ('0'<=ch&&ch<='9') tot=tot*10+ch-48,ch=getchar();    x=tot*f;    return Eoln(ch);}void msort(int L,int R) //二路归并{    if (L>=R) return;int mid=L+(R-L>>1);    msort(L,mid);msort(mid+1,R);    for (int k=L;k<=R;k++) c[k]=b[k];    int i=L,j=mid+1;    for (int k=L;k<=R;k++)        if (j>R||i<=mid&&c[i]<=c[j]) b[k]=c[i++]; else        b[k]=c[j++],ans=(ans+mid-i+1)%tt;}void put_s(int x,int y,double ti,double dis) //放入堆中{    int son;heap_s[++len_s]=(Heap){x,y,ti,dis};son=len_s;    while (son!=1&&heap_s[son]<heap_s[son>>1])    {        swap(heap_s[son],heap_s[son>>1]);        son>>=1;    }}void del_s() //删除堆顶{    int fa=1,son;heap_s[1]=heap_s[len_s--];    while (2*fa<=len_s)    {        if (2*fa+1>len_s||heap_s[2*fa]<heap_s[2*fa+1]) son=2*fa; else son=2*fa+1;        if (heap_s[son]<heap_s[fa])        {            swap(heap_s[son],heap_s[fa]);            fa=son;        } else break;    }}double getti(int i,int j) {return (double)(a[j].x-a[i].x)/(a[i].v-a[j].v);} //得到i和j相遇的时间double getdis(int i,int j) {return a[i].x+getti(i,j)*a[i].v;} //得到i和j相遇的位置int main(){    freopen("program.in","r",stdin);    freopen("program.out","w",stdout);    readi(n);    for (int i=1;i<=n;i++) readi(a[i].x),readi(a[i].v);    memcpy(b,a,sizeof(b)); //备份一个b用来二路归并求逆序对    msort(1,n);    printf("%d\n",ans);    for (int i=1;i<=n;i++) l[i]=i-1,r[i]=i+1;    for (int i=1;i<=n-1;i++) if (a[i].v>a[i+1].v)        put_s(i,i+1,getti(i,i+1),getdis(i,i+1)); //相邻建边,加入堆中    int te=1;    while (len_s&&te<=10000)    {        int x=heap_s[1].x,y=heap_s[1].y,lx=l[x],ry=r[y];del_s(); //得到堆顶        if (r[x]!=y||l[y]!=x) continue; //判断该堆顶是否“过期”,也就是判重        printf("%d %d\n",x,y);te++;        if (ry!=n+1) {l[ry]=x;if (a[x].v>a[ry].v) put_s(x,ry,getti(x,ry),getdis(x,ry));} //x和y交换过后,x和r[y]产生新状况        if (lx) {r[lx]=y;if (a[lx].v>a[y].v) put_s(lx,y,getti(lx,y),getdis(lx,y));} //x和y交换后,y和l[x]产生新状况        l[x]=y;r[y]=x;r[x]=ry;l[y]=lx; //修正链表    }    return 0;}
2 0
原创粉丝点击