其他分享
首页 > 其他分享> > 「JOISC 2019 Day3」穿越时空 Bitaro

「JOISC 2019 Day3」穿越时空 Bitaro

作者:互联网

「JOISC 2019 Day3」穿越时空 Bitaro

数据结构,线段树

考虑每次行动

显然暴力贪心走是正确的

但时间的流逝很麻烦

可以将每对 \([L_i, R_i] \longrightarrow [L_i - i, R_i - i]\)

这样就可以抵消掉时间流动的问题,就像这样

对于当前时间 \(t\) ,当前这条路时间 \([L_i, R_i]\)

现在每次操作是 \(O(n)\),总\(O(nq)\) ,可以获得 \(\color{lightgreen}4\) 分的好成绩

接下来考虑多次询问和修改

我们可以用一个三元组 \((a, b, c)\) 来表示一次行动,其中 \(a\) 表示开始时刻,\(b\) 表示结束时刻,\(c\) 表示倒流时间数

显然,对于每次询问,我们只需求出对应三元组的 \(c\) 即可

试着将一个三元组拆成两个有序的更小的三元组,这样我们可以将询问的三元组一直拆下去,直到可知为止

反过来考虑将两个三元组 \((a_1, b_1, c_1)\) 和 \((a_2, b_2, c_2)\) 按序合并为一个三元组

如图,前后两次行动可以接上中间的差值,合并为一次行动

\((a_1, b_1, c_1) + (a_2, b_2, c_2) \longrightarrow (a_1, b_2, c_1 + c_2 + \Delta c)\)

但是题目给的是一个二元区间 \([L_i, R_i]\) ,考虑合并两个二元区间会变成什么

当两个区间有交集时,合并和便是个更小的区间,也就是指只有在这个更小的区间才能通过这两个区间,比如

图中转化为:

\([L_1, R_1] + [L_2, R_2] \longrightarrow [\max(L_1, L_2), \min(R_1, R_2)]\)

当两个区间无交集时,遍合并为了三元组,表示通过这两个区间需要代价(等待或者时间倒流),比如

图中转化为:

\([L_1, R_1] + [L_2, R_2] \longrightarrow (L_1, R_2, L_1 - R_2)\)

现在考虑二元与三元合并

红色的是合并后的三元组

以及三元与二元合并

合并,修改,查询,想到用线段树维护

所以每次查询 \(A, B, C, D\),只需将范围内的所有区间合并起来就行了,但由于起点终点有时间限制,所有在构造两个区间与之并起来,就能得到此次行动的三元组

\([B - A, B - A] + \text{query}(A, C- 1) + [D - C, D - C]\)

减 \(A\) 减 \(C\) 是减去位置,抵消流动时间

特别注意的地方,就是询问中会有反向的,也就是从后边的城市到前面的城市

对于这些询问,只需将其提出来,反向建线段树再做就行了

可以获得 \(\color{red}{100}\) 分的好成绩

#include <bits/stdc++.h>
#define re register
// #define int long long
// #define pair pair<int, int>
#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout);
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 21], *p1 = buf, *p2 = buf;
using namespace std;
inline int read()
{
    re int x = 0, f = 0;
    re char ch = getchar();
    while (!isdigit(ch)) {if (ch == '-') f = 1; ch = getchar();}
    while (isdigit(ch)) {x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
    return f ? -x : x;
}
inline string getstr()
{
    string res = "";
    re char ch = getchar();
    while (isspace(ch)) ch = getchar();
    while (!isspace(ch)) res.push_back(ch), ch = getchar();
    return res;
}
const int N = 3e5 + 5, P = 1e9 + 9;
int n;
struct node
{
    long long a, b, c;
    inline node operator + (const node &x) const
    {
        node res;
        if (a == 0 && b == 0 && c == 0) return x;
        if (x.a == 0 && x.b == 0 && x.c == 0) return *this;
        if (c == -1 && x.c == -1)
        {
            if (a > x.b) res = {a, x.b, a - x.b};
            else if (b < x.a) res = {b, x.a, 0};
            else res = {max(a, x.a), min(b, x.b), -1};
        }
        else if (c == -1)
        {
            if (b < x.a) res = {b, x.b, x.c};
            else if (a > x.a) res = {a, x.b, x.c + a - x.a};
            else res = x;
        }
        else if (x.c == -1)
        {
            if (b < x.a) res = {a, x.a, c};
            else if (b > x.b) res = {a, x.b, c + b - x.b};
            else res = *this;
        }
        else res = {a, x.b, max(b - x.a, 0ll) + c + x.c};
        return res;
    }
};
node tr[N << 2], A[N];
inline void built(int p, int l, int r)
{
    if (l > r) return;
    if (l == r) return void(tr[p] = {A[l].a - l, A[l].b - l - 1, -1});
    int mid = l + r >> 1;
    built(p << 1, l, mid);
    built(p << 1 | 1, mid + 1, r);
    tr[p] = tr[p << 1] + tr[p << 1 | 1];
}
inline void update(int p, int l, int r, int LR, node val)
{
    if (l == r) return void(tr[p] = val);
    int mid = l + r >> 1;
    if (LR <= mid) update(p << 1, l, mid, LR, val);
    else update(p << 1 | 1, mid + 1, r, LR, val);
    tr[p] = tr[p << 1] + tr[p << 1 | 1];
}
inline node ask(int p, int l, int r, int L, int R)
{
    if (L <= l && r <= R) return tr[p];
    int mid = l + r >> 1;
    node res = {0, 0, 0};
    if (L <= mid) res = res + ask(p << 1, l, mid, L, R);
    if (mid < R) res = res + ask(p << 1 | 1, mid + 1, r, L, R);
    return res;
}
long long ans[N];
struct
{
    int op, A, C, B, D;
}q[N];
signed main()
{
    bool flag = 1;
    n = read(); int T = read();
    for (re int i = 1; i < n; ++i) A[i] = {read(), read(), -1};
    built(1, 1, n - 1);
    for (re int i = 1; i <= T; ++i)
    {
        ans[i] = -1;
        q[i].op = read(), q[i].A = read(), q[i].B = read(), q[i].C = read();
        if (q[i].op == 1) update(1, 1, n - 1, q[i].A, {q[i].B - q[i].A, q[i].C - q[i].A - 1, -1});
        else
        {
            q[i].D = read();
            if (q[i].A == q[i].C) {ans[i] = max(0, q[i].B - q[i].D); continue;}
            if (q[i].A > q[i].C) {flag = 1; continue;}
            node res = ask(1, 1, n - 1, q[i].A, q[i].C - 1);
            res = node{q[i].B - q[i].A, q[i].B - q[i].A, -1} + res;
            res = res + node{q[i].D - q[i].C, q[i].D - q[i].C, -1};
            ans[i] = res.c;
        }
    }
    if (flag)
    {
        reverse(A + 1, A + n);
        built(1, 1, n - 1);
        for (re int i = 1; i <= T; ++i)
        {
            if (q[i].op != 1) q[i].C = n - q[i].C + 1, q[i].A = n - q[i].A + 1;
            else q[i].A = n - q[i].A;
        }
        for (re int i = 1; i <= T; ++i)
        {
            if (ans[i] != -1) continue;
            if (q[i].op == 1) update(1, 1, n - 1, q[i].A, {q[i].B - q[i].A, q[i].C - q[i].A - 1, -1});
            else
            {
                node res = ask(1, 1, n - 1, q[i].A, q[i].C - 1);
                res = node{q[i].B - q[i].A, q[i].B - q[i].A, -1} + res;
                res = res + node{q[i].D - q[i].C, q[i].D - q[i].C, -1};
                ans[i] = res.c;
            }
        }	
    }
    for (re int i = 1; i <= T; ++i) if (q[i].op != 1) printf("%lld\n", ans[i]);
    return 0;
}

标签:res,合并,Day3,else,JOISC,2019,区间,三元组,define
来源: https://www.cnblogs.com/After-glow/p/15836763.html