「JOISC 2020 Day4」治疗计划

传送门


思路

淼淼题

考虑一个朴素的 DP:我们设 \(f_i\) 表示将 \([1,R_i]\) 的居民治好需要的最小代价

对于转移 \(f_i\longrightarrow f_j\),当且仅当 \(R_i-L_j+1\ge |T_j-T_i|\)

这是 \(O(n^2)\) 的;因为有取 \(\min\) 操作,我们考虑用最短路优化

观察式子,我们可以分两类讨论:

  1. \(T_i\ge T_j\) 时,有 \(R_i-T_i+1\ge L_j-T_j\)

  2. \(T_i < T_j\) 时,有 \(R_i+T_i+1\ge L_j+T_j\)

我们考虑建两棵线段树,每从一个点出发时,我们就在树上查询可以转移到的点

但这个复杂度似乎还是不对?

实际上,由于边权都是目标点的点权,结合 dijkstra 过程,当转移到目标点时,这个目标点一定就取到了最短路,因此每个点只会被转移一次

被转移后就在线段树上进行修改(改为 INF


#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
#define PFOR(i, x) for(int i = he[x]; i; i = r[i].nxt)
inline int rd()
{
    int sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
const int INF = 0x7fffffff;
int n, m, maxt;
struct Node
{
    int t, l, r, c;
}a[100005]; LL ans = 1e18;
#define pli std::pair<LL, int>
std::priority_queue<pli, std::vector<pli>, std::greater<pli>> q;
LL ndis; std::bitset<100005> vis;
namespace Seg
{
    #define ls (now << 1)
    #define rs  ((now << 1) | 1)
    int Mn1[400005], Mn2[400005];
    inline void up(int now) {Mn1[now] = std::min(Mn1[ls], Mn1[rs]), Mn2[now] = std::min(Mn2[ls], Mn2[rs]);}
    void build(int now, int l, int r)
    {
        if(l == r) return void((Mn1[now] = a[l].l - a[l].t, Mn2[now] = a[l].l + a[l].t));
        int mid = (l + r) >> 1;
        build(ls, l, mid), build(rs, mid + 1, r);
        up(now);
    }
    void queryL(int now, int l, int r, int to, int val)
    {
        if(Mn1[now] > val) return;
        if(l == r)
        {
            q.push(pli(ndis + a[l].c, l));
            Mn1[now] = Mn2[now] = INF;
            return;
        }
        int mid = (l + r) >> 1;
        queryL(ls, l, mid, to, val);
        if(mid < to) queryL(rs, mid + 1, r, to, val);
        up(now);
    }
    void queryR(int now, int l, int r, int to, int val)
    {
        if(Mn2[now] > val) return;
        if(l == r)
        {
            q.push(pli(ndis + a[l].c, l));
            Mn1[now] = Mn2[now] = INF;
            return;
        }
        int mid = (l + r) >> 1;
        if(to <= mid) queryR(ls, l, mid, to, val);
        queryR(rs, mid + 1, r, to, val);
        up(now);
    }
}
inline void dij()
{
    FOR(i, 1, m) if(a[i].l == 1)
        q.push(pli(a[i].c, i));
    while(!q.empty())
    {
        while(!q.empty() && vis[q.top().second]) q.pop();
        if(q.empty()) break;
        int now = q.top().second; ndis = q.top().first;
        vis[now] = 1; q.pop();
        if(a[now].r == n) ans = std::min(ans, ndis);
        if(now > 1) Seg::queryL(1, 1, m, now - 1, a[now].r - a[now].t + 1);
        if(now < m) Seg::queryR(1, 1, m, now + 1, a[now].r + a[now].t + 1);
    }
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = rd(), m = rd();
    FOR(i, 1, m) a[i] = (Node){rd(), rd(), rd(), rd()};
    std::sort(a + 1, a + 1 + m, [&](Node a, Node b){return a.t < b.t;});
    Seg::build(1, 1, m);
    dij();
    ans == 1e18 ? puts("-1") : printf("%lld", ans);
    return 0;
}
posted @ 2022-09-21 16:03  zuytong  阅读(60)  评论(0编辑  收藏  举报