题意:

\(n\)个房屋,有\(m\)种治疗方案。
一开始所有人都得了病。
每种治疗方案给出:\(t_i,l_i,r_i,c_i\)。表示第\(t_i\)天晚上选择治疗\([l_i,r_i]\)的人花费\(c_i\)
每天早上得病的人都会往左右相邻传染一个。
问把所有人病治好的最小花费。

思路:

治好相当于选择的每个方案能“无缝衔接”。
向能衔接的方案连边。
具体\(i\)\(j\)连边:

  • \(t_i\ge t_j\): \(r_i-l_j+1\ge t_i-t_j\)\(r_i-T_i+1\ge l_j-t_j\)
  • \(t_i\le t_j\): \(r_i-l_j+1\ge t_j-t_i\)\(r_i+t_i+1\ge t_j+l_j\)
    然后可以线段树优化建图跑最短路。复杂度\(O(nlog^2n)\)
    其实因为最短路更新的点权恒定为\(c_i\),所以跑Dijkstra()每个点只会被松弛一次。
    因此建两棵权值\(l_i-t_i\)\(l_i+t_i\)的线段树,每次取出堆顶,在线段树上松弛下一个点,加入堆中。
    被松弛的点就可以删去(线段树上权值赋为inf)
    具体线段树权值存最小值,代表如果最小值都比当前比较值大那就不用找下去了,这样每一个点只会被更一次,复杂度\(log_2n\)找到这个点。

代码

点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+5;
const int inf=2e9+6;
struct node {int t,l,r,c;}a[N];
struct seg {int l,r,mn1,mn2;}T[N];
bool cmpT(node u,node v) {return u.t<v.t;}
struct pq {
	int p;ll w;
	bool operator<(const pq &u) const{return w>u.w;}
};
priority_queue<pq> Q;
int n,m,ls[N],rs[N],nd,tt[N];
void P_up(int x) {T[x].mn1=min(T[ls[x]].mn1,T[rs[x]].mn1);T[x].mn2=min(T[ls[x]].mn2,T[rs[x]].mn2);}
void Build(int &x,int l,int r) {
	x=++nd;T[x]=(seg){l,r,inf,inf};
	if(l==r) {
		if(a[l].l==1)return;
		T[x].mn1=a[l].l+a[l].t;T[x].mn2=a[l].l-a[l].t;return;
	}
	int mid=(l+r)>>1;
	Build(ls[x],l,mid),Build(rs[x],mid+1,r);
	P_up(x);
}
int up;
ll w;
void _fdL(int x,int p,int q) {
	if(T[x].mn2>up) return;
	if(T[x].l==T[x].r) {
		int k=T[x].l;Q.push((pq){k,w+a[k].c});
//		printf("to %d\n",k);
		T[x].mn1=T[x].mn2=inf;
		return;
	}
	int mid=(T[x].l+T[x].r)>>1;
	if(p<=mid)_fdL(ls[x],p,q);
	if(q>mid)_fdL(rs[x],p,q);
	P_up(x);
}
void _fdR(int x,int p,int q) {
	if(T[x].mn1>up) return;
	if(T[x].l==T[x].r) {
		int k=T[x].l;Q.push((pq){k,w+a[k].c});
		T[x].mn1=T[x].mn2=inf;
		return;
	}
	int mid=(T[x].l+T[x].r)>>1;
	if(p<=mid)_fdR(ls[x],p,q);
	if(q>mid)_fdR(rs[x],p,q);
	P_up(x);
}
void DJ() {
	int rt;Build(rt,1,m);
	for(int i=1;i<=m;i++) {
		if(a[i].l!=1) continue;
		Q.push((pq){i,a[i].c});
	}
	ll ans=1e18;
	while(!Q.empty()) {
		int u=Q.top().p;w=Q.top().w;Q.pop();
//		printf("%d %lld\n",u,w);
		if(a[u].r==n) {ans=min(ans,w);continue;}
		int k=upper_bound(tt+1,tt+1+m,a[u].t)-tt-1;		//the last <=a[u].t
//		printf("#k = %d\n",k);
		up=a[u].r-a[u].t+1;_fdL(1,1,k);
		if(k!=m){up=a[u].r+a[u].t+1;_fdR(1,k+1,m);}
	}
	printf("%lld",(ans==1e18)?-1:ans); 
}

int main() {
//	freopen("03-05.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) {scanf("%d%d%d%d",&a[i].t,&a[i].l,&a[i].r,&a[i].c);}
	sort(a+1,a+1+m,cmpT);
//	for(int i=1;i<=m;i++)printf("(%d:) t=%d l=%d r=%d c=%d\n",i,a[i].t,a[i].l,a[i].r,a[i].c);
	for(int i=1;i<=m;i++) {tt[i]=a[i].t;}
	DJ();
	return 0;
}