JOISC 2020 治療計画 做题记录

Solution

edu.

link

首先将感染的过程看作区间不断缩小,然后发现一个区间选择方案合法的充要条件是,他们可以完全覆盖 \([1, n]\) 且拼接时相邻两个区间有交。

不妨从 \(1\) 开始往后覆盖,则此时 \(j\) 可以拼在 \(i\) 后面当且仅当 \(r_i - l_j + 1 \ge |t_i - t_j|\)。设 \(S = \{i | l_i = 1\}, T = \{i | r_i = n\}\),且让满足上述条件的 \((i, j)\) 连边 \(i \to j\),则此时相当于 \(S \to T\) 的最短路,直接连边并跑最短路是 \(O(n^2 \log n)\) 的。

然后考虑模拟 dijkstra 的过程。这里有一个 \(\rm trick\): 权在点上的最短路每个点至多被松弛一次。这是因为 \(dis_u = dis_v + c_u\) 这个过程 \(c_u\) 是不变的,值的大小只取决于 \(dis_v\),而每次我们取最小的 \(dis_v\) 松弛,所以之后的点显然不优。

那么我们先拆掉绝对值,分为两种情况讨论,然后按 \(t_i\) 排序建出势能线段树,加速该过程即可。

qwq
#include<bits/stdc++.h>
#define ll long long
#define pb emplace_back
#define pir pair<ll, int>
#define fi first
#define se second
#define inv(x) qpow(x, mod - 2)
#define il inline
#define mkpir make_pair
#define ull unsigned long long
#define umap unordered_map
using namespace std;

const int N = 1e5 + 10, M = 2e5 + 10;
const ll mod = 998244353, INF = 1e18;

/*
struct edge{
  int v, next;
}edges[M << 1];
int head[N], idx;

void add_edge(int u, int v){
  edges[++idx] = {v, head[u]};
  head[u] = idx;
}
*/

il ll qpow(ll x, ll y){
  ll ret = 1;
  for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
  return ret;
}
il void chkmin(ll& x, ll y){if(y < x) x = y;}
il void chkmax(ll& x, ll y){if(y > x) x = y;}
il void chkmin(int& x, int y){if(y < x) x = y;}
il void chkmax(int& x, int y){if(y > x) x = y;}
il void chkmod(ll& x){x = (x + mod) % mod;}
il void ADD(ll& x, ll y){x += y; (x >= mod) ? (x -= mod) : 0;}
il void MUL(ll& x, ll y){x = x * y % mod;}
//#define int long long

int n, m, rk[N];
ll dis[N]; 

struct option{
  int l, r, t; 
  ll c;
}a[N];

priority_queue<pir, vector<pir>, greater<pir> > Q;

bool cmp(struct option o1, struct option o2){return o1.t < o2.t;}

struct Segtree{
  #define ls (o << 1)
  #define rs (o << 1 | 1)
  #define mid ((l + r) >> 1)
  int tag[N << 2];
  ll mn[N << 2][2];
  void pushup(int o){
    for(int i = 0; i < 2; i++) mn[o][i] = min(mn[ls][i], mn[rs][i]);
    tag[o] = (tag[ls] & tag[rs]);
  }
  void build(int o, int l, int r){
    if(l == r){
      if(a[l].l == 0){tag[o] = 1; mn[o][0] = mn[o][1] = INF; return;}
      mn[o][0] = a[l].l - a[l].t; mn[o][1] = a[l].l + a[l].t; 
      return;
    }
    build(ls, l, mid); build(rs, mid + 1, r);
    pushup(o);
  }
  void kil(int o, int l, int r, int op, int lim, ll val){
    if(mn[o][op] > lim || tag[o]) return;
    if(l == r){mn[o][0] = mn[o][1] = INF; tag[o] = 1; dis[l] = val + a[l].c; Q.push({dis[l], l}); return;}
    kil(ls, l, mid, op, lim, val); kil(rs, mid + 1, r, op, lim, val);
    pushup(o);
  }
  void findSeg(int o, int l, int r, int s, int t, int op, int lim, ll val){
    if(tag[o] || mn[o][op] > lim || s > t) return;
    if(s <= l && r <= t) return kil(o, l, r, op, lim, val);
    if(s <= mid) findSeg(ls, l, mid, s, t, op, lim, val);
    if(mid < t) findSeg(rs, mid + 1, r, s, t, op, lim, val);
    pushup(o);
  }
}tr;

signed main(){
  ios::sync_with_stdio(0);
  cin.tie(0); cout.tie(0);
  cin >> n >> m;
  for(int i = 1; i <= m; i++) cin >> a[i].t >> a[i].l >> a[i].r >> a[i].c, a[i].l--;
  sort(a + 1, a + m + 1, cmp);
  for(int i = 1; i <= m; i++){
    if(a[i].l == 0) dis[i] = a[i].c, Q.push(mkpir(dis[i], i));
    else dis[i] = INF;
  } tr.build(1, 1, m);
  while(!Q.empty()){
    int i = Q.top().se; Q.pop();
    tr.findSeg(1, 1, m, 1, i - 1, 0, a[i].r - a[i].t, dis[i]);
    tr.findSeg(1, 1, m, i + 1, m, 1, a[i].r + a[i].t, dis[i]);
  } ll ans = INF;
  for(int i = 1; i <= m; i++) if(a[i].r == n) chkmin(ans, dis[i]);
  if(ans != INF) cout << ans;
  else cout << -1;

  return 0;
}

posted @ 2025-09-19 15:44  Little_corn  阅读(2)  评论(0)    收藏  举报