【题目大意】
本题的大意就是有n个飞船,给出飞船的位置和速度,求飞船之间超越的总次数(结果要mod 1000000)和两个互相超越的飞船的编号(按时间大小排序,如果超越的总次数大于10000则输出前10000个)
【解题报告】
求飞船之间超越的总次数逆序对即可。
两个互相超越的飞船的编号:
用小根堆来做,按照飞船x超越飞船y的时间的大小建堆。
每次从堆顶取出一组超越的编号,则先验证编号是否成立,成立便将这两艘飞船的实时位置交换,并判断交换后这两艘飞船是否可以超越相邻的飞船或被相邻的飞船超越,如果验证成功则将这组超越的编号放进堆中。
注:
1.实时位置为目前飞船所在的位置(即经过?次超越或被超越后,飞船所在的位置)
2.输出两个互相超越的飞船的编号中注意当超越的时间相同时,按照超越的飞船的实时位置从小到大排序,而不是初始位置。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=250005,maxm=10005,maxv=105,tt=1000000;
int n,num,len,ans,max_v,c[maxv],where[maxn];
struct wjd
{
int x,y,id;
}a[maxn];
struct ha
{
double x;
int s,t;
}hep[maxn+maxm];
inline int read_()
{
int sum=0;
char ch=getchar();
while (ch<'0'||ch>'9') ch=getchar();
while (ch>='0'&&ch<='9') sum=sum*10+ch-48,ch=getchar();
return sum;
}
void put_(double x,int s,int t)
{
hep[++len].x=x; hep[len].s=s; hep[len].t=t; int son=len;
while (son!=1&&(hep[son].x<hep[son>>1].x||(hep[son].x==hep[son>>1].x&&where[hep[son].s]<where[hep[son>>1].s]))) swap(hep[son],hep[son>>1]),son>>=1; //按照超越时间为第一关键字,实时距离为第二关键字建小根堆
}
ha get_()
{
ha sum=hep[1];
hep[1]=hep[len--];
int fa=1,son;
while (fa*2<=len)
{
if (fa*2+1>len||(hep[fa*2].x<hep[fa*2+1].x||(hep[fa*2].x==hep[fa*2+1].x&&where[hep[fa*2].s]<where[hep[fa*2+1].s]))) son=fa*2; else son=fa*2+1;
if (hep[son].x<hep[fa].x||(hep[son].x==hep[fa].x&&where[hep[son].s]<where[hep[fa].s]))
{
swap(hep[son],hep[fa]);
fa=son;
}
else break;
}
return sum;
}
int lowbit_(int x)
{
return x&(-x);
}
int get_(int x)//树状数组
{
int sum=0;
while (x<=max_v)
{
sum+=c[x];
x+=lowbit_(x);
}
return sum;
}
void add_(int x,int da)//树状数组
{
while (x>0)
{
c[x]+=da;
x-=lowbit_(x);
}
}
int main()
{
freopen("2274.in","r",stdin);
freopen("2274.out","w",stdout);
n=read_(); len=ans=num=max_v=0;
for (int i=1; i<=n; i++) a[i].x=read_(),a[i].y=read_(),a[i].id=i,where[i]=i,max_v=max(max_v,a[i].y);
memset(c,0,sizeof(c));
for (int i=1; i<=n; i++) ans=(ans+get_(a[i].y))%tt,add_(a[i].y-1,1);//树状数组求逆序对
printf("%d\n",ans);
for (int i=1; i<n; i++)
{
if (a[i].y<=a[i+1].y) continue;
put_((double)(a[i+1].x-a[i].x)/(a[i].y-a[i+1].y),i,i+1);
}
num=0;
while (num<10000&&len!=0)
{
ha x=get_();
if ((where[x.s]+1!=where[x.t])) continue;//判断已经不存在的情况
printf("%d %d\n",x.s,x.t);
int s=where[x.s],t=where[x.t]; where[x.s]=t; where[x.t]=s;//记录实时位置
swap(a[s],a[t]);//将超越的两辆飞船交换位置
if (s>1&&(a[s-1].y>a[s].y)) if (where[a[s-1].id]+1==where[a[s].id]) put_((double)(a[s].x-a[s-1].x)/(a[s-1].y-a[s].y),a[s-1].id,a[s].id);
if (t<n&&(a[t].y>a[t+1].y)) if (where[a[t].id]+1==where[a[t+1].id]) put_((double)(a[t+1].x-a[t].x)/(a[t].y-a[t+1].y),a[t].id,a[t+1].id);//判断超越后是否与新的相邻的飞船产生超越情况
++num;
}
return 0;
}
逆序对与二叉堆在Poj2274中的应用

博客介绍了如何利用逆序对思想解决Poj2274问题,该问题涉及飞船超越的总次数计算。通过建立小根堆,按照超越时间排序飞船编号,不断验证和更新飞船位置,来找到超越事件。当超越次数超过10000时,输出前10000组结果,注意相同时间的超越事件中,飞船实时位置决定了排序顺序。
1584

被折叠的 条评论
为什么被折叠?



