「JOISC 2020 Day4」治疗计划(线段树+dijkstra最短路)

本文介绍了一种解决医疗资源分配问题的算法,通过计算每个治疗方案的花费和可行性,确定如何合并方案以最小化总费用。利用线段树和Dijkstra算法找到从起点到终点的最短路径,确保治疗区域完整。核心在于判断方案合并条件和维护区间关系,以求得从L=1到R=n的完整健康区间最短治疗路径。
摘要由CSDN通过智能技术生成

「JOISC 2020 Day4」治疗计划

description

solution

d p i : 1 − R i dp_i:1-R_i dpi:1Ri 都能被救治成功的最小花费

两个治疗方案 [ L i , R i ] , [ L j , R j ] [L_i,R_i],[L_j,R_j] [Li,Ri],[Lj,Rj]能够合并成完整的健康区间的条件为 R i − L j + 1 ≥ ∣ T i − T j ∣ R_i-Lj+1\ge |T_i-T_j| RiLj+1TiTj

最后要合并成 [ 1 , n ] [1,n] [1,n]

相当于如果从 L i = 1 L_i=1 Li=1的所有方案出发,如果能与 j j j方案合并,则连边 i → c j j i\rightarrow^{c_j} j icjj

求到 R j = n R_j=n Rj=n的最短路

  • T i ≥ T j T_i\ge T_j TiTj

    R i − L j + 1 ≥ T i − T j ⇔ R i − T i + 1 ≥ L j − T j R_i-L_j+1\ge T_i-T_j\Leftrightarrow R_i-T_i+1\ge L_j-T_j RiLj+1TiTjRiTi+1LjTj

  • T i < T j T_i<T_j Ti<Tj

    R i − L j + 1 ≥ T j − T j ⇔ R i + T i + 1 ≥ L j + T j R_i-L_j+1\ge T_j-T_j\Leftrightarrow R_i+T_i+1\ge L_j+T_j RiLj+1TjTjRi+Ti+1Lj+Tj

T j T_j Tj为下标建线段树,维护两棵线段树

最短路用dijkstra跑,线段树可以查找出所有 i i i的出边点

因为每个点的花费是一定的,所以一定是当前最短路拓展到该点且仅一次

所以每个点只会被用一次(用了就可以删掉了)

O ( n log ⁡ n ) O(n\log n) O(nlogn)

code

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define inf 0x7f7f7f7f
#define int long long
#define maxn 100005
struct node {
	int t, l, r, c;
}cure[maxn];
vector < int > nxt;
priority_queue < pair < int, int > > q;
int n, m;
int dis[maxn];

bool cmp( node x, node y ) {
	return x.t < y.t;
}

class SegMentTree {
	private :
		int t[maxn << 2];
	public :
		void build( int num, int l, int r, int k ) {
			if( l == r ) {
				t[num] = cure[l].l + k * cure[l].t;
				return;
			}
			int mid = ( l + r ) >> 1;
			build( num << 1, l, mid, k );
			build( num << 1 | 1, mid + 1, r, k );
			t[num] = min( t[num << 1], t[num << 1 | 1] );
		}
		void modify( int num, int l, int r, int pos ) {
			if( l == r ) {
				t[num] = inf;
				return;
			}
			int mid = ( l + r ) >> 1;
			if( pos <= mid ) modify( num << 1, l, mid, pos );
			else modify( num << 1 | 1, mid + 1, r, pos );
			t[num] = min( t[num << 1], t[num << 1 | 1] );
		}
		void query( int num, int l, int r, int L, int R, int k ) {
			if( L > R || R < l || r < L || t[num] > k ) return;
			if( l == r ) {
				nxt.push_back( l );
				return;
			}
			int mid = ( l + r ) >> 1;
			query( num << 1, l, mid, L, R, k );
			query( num << 1 | 1, mid + 1, r, L, R, k );
		}
}S, T;
signed main() {
	scanf( "%lld %lld", &n, &m );
	for( int i = 1;i <= m;i ++ )
		scanf( "%lld %lld %lld %lld", &cure[i].t, &cure[i].l, &cure[i].r, &cure[i].c );
	sort( cure + 1, cure + m + 1, cmp );
	S.build( 1, 1, m, -1 );
	T.build( 1, 1, m, 1 );
	for( int i = 1;i <= m;i ++ )
		if( cure[i].l == 1 ) {
			dis[i] = cure[i].c;
			q.push( make_pair( -dis[i], i ) );
			S.modify( 1, 1, m, i );
			T.modify( 1, 1, m, i );
		}
	while( ! q.empty() ) {
		int u = q.top().second; q.pop();
		if( cure[u].r == n ) return ! printf( "%lld\n", dis[u] );
		nxt.clear();
		S.query( 1, 1, m, 1, u - 1, cure[u].r - cure[u].t + 1 );
		T.query( 1, 1, m, u + 1, m, cure[u].r + cure[u].t + 1 );
		for( int i = 0;i < nxt.size();i ++ ) {
			int v = nxt[i];
			dis[v] = dis[u] + cure[v].c;
			q.push( make_pair( -dis[v], v ) );
			S.modify( 1, 1, m, v );
			T.modify( 1, 1, m, v );
		}
	}
	printf( "-1\n" );
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值