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))。

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

每次取出堆顶,输出。

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

接下来见代码。

嗲吗

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#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 我