COCI CONTEST #3 29.11.2014 KAMIONI

题目描述

A国的城市可以看做是数轴上的整数点,有n辆卡车在公路上往返行驶,它们只能在到达某个城市才能掉头,掉头是不需要时间的,而且卡车在途中不能停下来。这n辆卡车初始时可能位于不同的城市,在某个时刻它们同时出发,每辆卡车每分钟可以到达下一个城市,即每分钟可以行使单位1的路程。当卡车到达终点时,它将会进入时空隧道,瞬间消失。给出每辆卡车的行驶的路径,即卡车的起点、中途每次掉头的点及终点,有m次查询,每次查询指定两辆卡车,问它们一共相遇多少次?
注意:卡车相遇点不一定是城市。另外,相遇时其中一辆卡车进入时光隧道不算相遇;起始瞬间两辆卡车位于同一地点不算相遇;其中一辆卡车正好掉头不算相遇。
n<=100000,m<100000,城市的编号在[1,10^9]之间。每辆卡车路径掉头的次数不超过3*10^5;所有卡车掉头的总次数之和不超过3*10^5.

输入:

第一行两个整数n,m,表示卡车的数量和查询的次数。
接下来n行,每行的第一个数为Ki(2<=Ki<=3*10^5),表示卡车的路径包含Ki个点,接下来有Ki个数,每个数在区间[1,10^9]之间,按顺序表示卡车行驶时经过的城市,包括起点和终点。
所有Ki之和超过3*10^5
接下来m行,每行两个整数,表示查询这两辆卡车一共相遇多少次。

输出:

对每次查询,输出一个整数,表示它们相遇次数。
50%的数据,n<100,Ki<1000,m<1000

思路:

首先我们应该将每一辆车的转折点按照发生时间来排序。讲每一个查询都保存下来,当然重复的我们只留一个去计算,其余的我们应当保存在一个与保留的那一个有关的数据结构里。顺便提一下,做这道题是一定要估计好空间复杂度,防止被卡空间。然后在保存一个询问的时候只保存在拐点少的那个的名下。然后枚举每一个拐点就可以了。
然后我们分析时间复杂度:
k使kk
k使k+k
通过以上分析,我们知道均摊的时间复杂度不会超过 k 。所以总的时间复杂度就是 O(k32) .

代码:(用hash表实现去重以及保存询问)

#include<algorithm>
#include<map>
#include<cstdio>
#include<vector>
#define LL long long
#define PII pair<int, int>
const int MAXN = 300010;
using namespace std;
struct event {
    int id, p, dir; //id->trucks' number; p->position; dir->direction
    LL time;
    bool operator < (const event &rhs) const
    {
        if(time != rhs.time) return time < rhs.time;
        return dir > rhs.dir;
    }
    event(){}
    event(int a, int b, int c, LL d){id = a, p = b, dir = c; time = d;}
};
int h[2000000];
PII hsh[2000000];
int hshit(PII t)
{
    int p = 1LL * (t.second - t.first) * t.first * 12580 % 2000000;
    while(h[p] && hsh[p] != t) p = (p + 1) % 2000000;
    ++ h[p];
    hsh[p] = t;
    return p;
}
struct query {
    int id, was_left, qid;
    query() {}
    query(int a, int b, int c) { id = a; was_left = b; qid = c; }
};
struct List {
    int id, num;
    vector<int> index;
    List *next;
    List(){next = NULL; id = num = -1;}
}*Adj[MAXN], *hs[2000000];
struct truck
{
    int k, p, dir;
    LL time;
    vector<query> queries;
} trucks[MAXN];
inline void GET(int &n)
{
    n = 0; char c;
    do c = getchar(); while(c > '9' || c < '0');
    while(c >= '0' && c <= '9') {n = n*10 + c - '0'; c = getchar();}
}
int n, m;
void init(event &e)
{
    trucks[e.id].p = e.p;
    trucks[e.id].dir = e.dir;
    trucks[e.id].time = e.time;
}
int check(const truck &a, const truck &b, LL pretime, int predir)
{
    if (!b.dir && pretime >= b.time)
        return 0;
    int b_pos, a_pos;
    if (!b.dir)
    {
        b_pos = b.p;
        a_pos = (a.time - b.time) *(-predir) + a.p;
    }
    else
    {
        a_pos = a.p;
        b_pos = (a.time - b.time) * b.dir + b.p;
    }
    return b_pos < a_pos ? -1 : 1;
}
int ans[MAXN];
void solve(const truck &t, query &q, LL pretime, int predir)
{
    int is_left = check(t, trucks[q.id], pretime, predir);
    ans[q.qid] += (q.was_left * is_left == -1);
    q.was_left = is_left;
}
int main()
{
    vector<event> events;
    GET(n); GET(m);
    int x, newx;
    LL tsum;
    event e;
    for(int i = 0; i < n; ++ i)
    {
        tsum = 0;
        GET(trucks[i].k); GET(x);
        for(int j = 0; j < trucks[i].k - 1; ++ j)
        {
            GET(newx);
            e = event(i, x, x < newx ? 1 : -1, tsum);
            if(!j) init(e);
            else events.push_back(e);
            tsum += abs(x - newx);
            x = newx;
        }
        e = event(i, x, 0, tsum);
        events.push_back(e);
    }
    int a, b;
    for(int i = 0; i < m; i ++)
    {
        GET(a); GET(b);
        -- a; -- b;
        if(a > b) swap(a, b);
        int t = hshit(PII(a, b));
        if(h[t] > 1) {
            hs[t]->num ++;
            hs[t]->index.push_back(i);
            continue;
        }
        else {
            if(!Adj[a]) {
                Adj[a] = new List();
                Adj[a]->num = 1;
                Adj[a]->id = b;
                hs[t] = Adj[a];
                hs[t]->index.push_back(i);
            }
            else {
                List *p = new List();
                p->next = Adj[a]->next;
                p->id = b;
                p->num = 1;
                Adj[a]->next = p;
                hs[t] = p;
                hs[t]->index.push_back(i);
            }
        }
        if (trucks[a].k > trucks[b].k) swap(a, b);
        trucks[a].queries.push_back(query(b, trucks[b].p < trucks[a].p ? -1 : 1, i));
    }
    sort(events.begin(), events.end());
    for(int i = 0; i < events.size(); i ++)
    {
        e = events[i];
        LL prev_time = trucks[e.id].time;
        int prev_dir = trucks[e.id].dir;
        init(e);
        for(int i = 0; i < trucks[e.id].queries.size(); i ++)
        {
            query &q = trucks[e.id].queries[i];
            solve(trucks[e.id], q, prev_time, prev_dir);
        }
    }
    int sz;
    for(int i = 0; i < n; i ++)
        for(List *p = Adj[i]; p; p = p->next) {
            sz = p->index.size();
            for(int j = 1; j < sz; j ++)
                ans[p->index[j]] = ans[p->index[0]];
        }
    for(int i = 0; i < m; ++i)
        printf("%d\n", ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值