[COCI 2014/2015 #3]KAMIONI

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

我的做法:

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

这里是官方题解:

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

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

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

程序:

#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;
}

转载于:https://www.cnblogs.com/QAQAQ/p/10925806.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值