JOISC 2020 治療計画 做题记录
Solution
edu.
首先将感染的过程看作区间不断缩小,然后发现一个区间选择方案合法的充要条件是,他们可以完全覆盖 \([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;
}