POJ2274题解报告

更多请见DUMBLOG

一只蒟蒻的POJ2274题解报告

题目链接

题目描述

给一堆车超出起点的距离 x 和车速度 v ,速度不变,求出飞行过程中的超车总数(对1000000取模)并输出超车与被超车的车的编号(如有同时则编号小的先输出)。

输入

一行一个整数 n (0 < n <= 250000)表示车数。

接下来 n 行,每行两个整数 x (0 <= x <= 1000000)和 v(0 < v < 100),表示车超出起点的距离和车速(保证编号 1~n 的车的 x 值单调递增)。

输出

第一行一个整数,表示超车次数对1000000取模的值。

后面 min(10000,超车次数) 行(即如果超车次数不足10000次,则全输出,否则输出前10000个)每行两个整数 a 和 b,表示超车的车的编号以及被超车的车的编号(即 a 车超过了 b 车)。如果有多个超车事件同时发生,先输出离起跑线近的那个(即排名小的)。

题解

第一个问题一看就是逆序对问题,可以用树状数组求。

第二个问题稍微麻烦些。

我们令离起跑线近的排名小。

我们将所有车按照其 x 值排序,如图:
请添加图片描述
我们发现,只有排名相邻的两辆车才会最先发生超车事件。

超车事件发生的条件比较容易推出来:

设被超车的车的编号是 X ,排名是 NUMX 。

那么只要当 排名为 NUMX-1 的车(上图中靠左的车辆)的速度 大于 X 的速度 就会发生超车。

请添加图片描述

如上图,最先发生超车的一定是 1-2,2-3,或 3-4。

假设此时只有 2-3 发生超车事件,超车交换位置后排名如下:

请添加图片描述

交换后又产生两组可能发生超车: 1-3 和 2-4 。所以接下来需要考虑这两个。

然后我们想:需要找最先超车的(即超车时间最小的),又要在中途加入操作对象,可以想到用堆来实现。

堆(小根堆)中存一个三元组(int,int,double),表示前一辆车(超车的车)的编号,后一辆车(被超车的车)的编号 以及 两车发生超车的时间((x1-x2)/(v2-v1))。

堆中排序的关键字就是超车时间和排名(注意时间相同时要按排名输出)

每次取出堆顶,输出。

然后判断交换位置后新产生的两组是否能发生超车,能则入堆。

接下来见代码。

嗲吗

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<stack>
#include<map>
#define PII pair<int,int>
#define int long long
using namespace std;
const int N = 1111500, MOD = 1e6, P = 131;
int read(){char c=getchar();int s=0,f=1;for(;!isdigit(c);c=getchar())if(c=='-')f=-1;for(;isdigit(c);c=getchar())s=s*10+c-48;return s*f;}
int n, tr[N], x[N], v[N], num[N], id[N];
//tr数组是树状数组
//x[i]表示排名为 i 的车离超过起点的距离
//v[i]表示排名为 i 的车的速度
//num[i]表示编号为 i 的车的排名
//id[i]表示排名为 i 的车的编号
struct node 
{//堆中的三元组
	int x, y;
	double t;
	bool operator <(const node &b)const {
		return t > b.t || (t == b.t && num[x] > num[b.x]);
	}//重载运算符,让堆变成小根堆(按照时间和排名排序)
};

priority_queue<node> q;

//接下来两个树状数组操作
void add(int x) 
{//添加
	for (; x <= 100; x += x & -x)tr[x]++;
}
int ask(int x) 
{//查询
	int ans = 0;
	for (; x; x -= x & -x)ans = (ans + tr[x]) % MOD;//记得取模
	return ans;
}

signed main() 
{
	
	n = read();
	int ans = 0;
	//ans用来记超车次数
	for (int i = 1; i <= n; i++) 
	{
		x[i] = read(), v[i] = read();
		
		num[i] = id[i] = i;
		//初始化
		ans = (ans + ask(100) - ask(v[i])) % MOD;
		add(v[i]);
		//树状数组操作
		if (i ^ 1 && v[i - 1] > v[i])
		//如果 i!=1 并且排名比他小一的车速度比他快
			q.push((node) { 
				i - 1, //前一辆车
				i, //后一辆车
				(double)(x[i] - x[i - 1]) / (v[i - 1] - v[i]) //超车时间 
			});//入堆
	}
	
	printf("%lld \n", ans);
	//输出超车次数
	
	int t = 1; //t用来记录个数,保证输出不超过10000个
	while (t <= 10000 && q.size()) 
	{
		int X = q.top().x, Y = q.top().y;
		q.pop();
		//X是前一辆车的编号,Y是后一辆车的编号
		
		if (num[X] + 1 != num[Y])continue;
		//如果两辆车的排名不相邻(不是最先发生超车)则跳过。
		
		printf("%lld %lld\n", X, Y); //输出
		t++;
		
		swap(id[num[X]], id[num[Y]]);
		swap(v[num[X]], v[num[Y]]);
		swap(x[num[X]], x[num[Y]]);
		swap(num[X], num[Y]);
		//交换 X 车与 Y 车的位置
		//交换后 X 车在前,Y 车在后(原本 Y 前 X 后)
		if (num[X] < n && v[num[X]] > v[num[X] + 1] &&
		        num[id[num[X]]] + 1 == num[id[num[X] + 1]])
		        //如果交换后 X 的排名不是 n (X 前还有车)
		        //并且 X 车速大于 X 前面那辆
		        //并且两辆车的排名相邻
		        //最后一句可能有点绕,多读几遍,手动模拟一下就好了
			q.push((node) {
				id[num[X]], //前一辆车编号
				id[num[X] + 1], //后一辆车编号
				(double)(x[num[X] + 1] - x[num[X]]) / (v[num[X]] - v[num[X] + 1]) //时间
			});



		if (num[Y] > 1 && v[num[Y] - 1] > v[num[Y]] &&
		        num[id[num[Y] - 1]] + 1 == num[id[num[Y]]])
		        //如果交换后 Y 的排名不是 1 (Y 后还有车)
		        //并且 Y 车速小于 Y 后面那辆
		        //并且两辆车的排名相邻
			q.push((node) {
				id[num[Y] - 1], //前一辆车
				id[num[Y]], //后一辆车
				(double)(x[num[Y]] - x[num[Y] - 1]) / (v[num[Y] - 1] - v[num[Y]]) //时间
			});
	}
	return 0; //结束
}

有错误请 D 我

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值