COCI2014/2015#3-KAMIONI

难得的没人做的水紫题。。

我的做法:

首先我们应该将每一辆车的转折点按照发生时间来排序。将所有不重复的查询都保存在拐点少的那个的名下。然后枚举每一个拐点就可以了。

这里是官方题解:

  1. 预处理每一个辆车的每一个转折点,以及方向,并且按照时间排序。

  2. 对于卡车i,绑定与它有查询关系的卡车,保存的时候保证i的转折点要小于与它绑定的其他卡车。显然,如果i的转折点大于某一辆卡车,它会被另外一辆卡车绑定。用这个去优化前面能过一半点的代码。‘

  3. 取出一个转折点,处理与对应的卡车有查询关系的卡车。

程序:

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
116
117
118
119
120
121
122
#include <bits/stdc++.h>
using namespace std;

typedef long long llint;
typedef pair<int, int> pii;
const int MAXN = 300100;

enum { LEFT = -1, ABDUCTED, RIGHT };

struct event {
int truck_id;
int x, direction;
llint time;
};

struct query {
int truck_id, was_left, query_id;
};

struct truck {
int k;
int x, direction;
llint time;
vector<query> queries;
};

truck trucks[MAXN];
int ans[MAXN];

void apply_event(const event &e) {
trucks[e.truck_id].x = e.x;
trucks[e.truck_id].direction = e.direction;
trucks[e.truck_id].time = e.time;
}

int check_is_left(const truck &a, const truck &b, llint prev_time,
int prev_direction) {
if (b.direction == ABDUCTED && prev_time >= b.time)
return ABDUCTED;
int b_pos, a_pos;
if (b.direction == ABDUCTED) {
b_pos = b.x;
a_pos = (a.time - b.time) * (-prev_direction) + a.x;
} else {
a_pos = a.x;
b_pos = (a.time - b.time) * b.direction + b.x;
}
assert(b_pos != a_pos);
if (b_pos < a_pos)
return LEFT;
return RIGHT;
}

void solve_query(const truck &t, query &q, llint prev_time,
int prev_direction) {
int is_left = check_is_left(t, trucks[q.truck_id], prev_time, prev_direction);
ans[q.query_id] += (q.was_left * is_left == -1);
q.was_left = is_left;
}

bool cmp(const event &a, const event &b) {
if (a.time != b.time)
return a.time < b.time;
return a.direction > b.direction;
}

int main() {
int n, m;
vector<event> events;
map<pii, vector<int>> same_queries;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i) {
int x;
llint sum_time = 0;
scanf("%d%d", &trucks[i].k, &x);
for (int j = 0; j < trucks[i].k - 1; ++j) {
int new_x;
scanf("%d", &new_x);
event e = {i, x, x < new_x ? RIGHT : LEFT, sum_time};
if (j == 0) {
apply_event(e);
} else {
events.push_back(e);
}
sum_time += abs(x - new_x);
x = new_x;
}
event e = {i, x, ABDUCTED, sum_time};
events.push_back(e);
}
for (int i = 0; i < m; ++i) {
int a, b;
scanf("%d%d", &a, &b);
--a;
--b;
if (a > b)
swap(a, b);
same_queries[{a, b}].push_back(i);
if ((int)same_queries[{a, b}].size() > 1)
continue;
if (trucks[a].k > trucks[b].k)
swap(a, b);
trucks[a].queries.push_back(
{b, trucks[b].x < trucks[a].x ? LEFT : RIGHT, i});
}
sort(events.begin(), events.end(), cmp);
for (const event &e : events) {
llint prev_time = trucks[e.truck_id].time;
int prev_direction = trucks[e.truck_id].direction;
apply_event(e);
for (query &q : trucks[e.truck_id].queries)
solve_query(trucks[e.truck_id], q, prev_time, prev_direction);
}
for (auto queries : same_queries) {
int res = ans[queries.second[0]];
for (int t : queries.second)
ans[t] = res;
}
for (int i = 0; i < m; ++i)
printf("%d\n", ans[i]);
return 0;
}

   转载规则


《COCI2014/2015#3-KAMIONI》 VinovnyyPoterpevshiy 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
POJ1741-Tree POJ1741-Tree
我们知道,树上两个点的LCA要么是当前根节点,要么不是。。所以两个点间的最短路径要么经过当前根节点,要么在一棵当前根节点的子树中。。 考虑点分治,于是在原来同一子树中的两个点必然在一次分治中变为路径经过当前根节点的两个点。 处理路径经过当
2019-09-22
下一篇 
GXOI/GZOI2019-lvxingzhe GXOI/GZOI2019-lvxingzhe
暴力:对于每个特殊点为起始点计算Dijkstra或spfa。。 然而这样有很多重复操作,很多路径会被重复走。 改善:考虑把特殊点分组,分组只计算两组间的最短路,组内不计算。显然,分为两组最优(如果分为多组的话其实和没有分组一样会走重复路径)
2019-09-22
  目录