【堆+链表】POJ2274[The Race]题解

11 篇文章 0 订阅
3 篇文章 0 订阅
这篇博客介绍了如何解决POJ2274比赛超车问题,通过将问题转化为逆序对并利用二路归并求解。解题的关键在于理解超车总是发生在相邻赛车之间,因此采用堆来动态维护超车时间,每次从堆中取出最小的超车时间并更新赛车位置。博客提供了详细的解题思路和示例程序。
摘要由CSDN通过智能技术生成

题目概述

给出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+10000

bool 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;
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值