题解[JOISC2020] 治療計画

Posted SharpnessV

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解[JOISC2020] 治療計画相关的知识,希望对你有一定的参考价值。

最关键的一步,转化为最短路。

对于每个方案我们看成一个点,对于两个方案 \\(i,j\\) ,当且仅当 \\(R_i-L_j+1\\ge|T_i-T_j|\\) 时,从点 \\(i\\) 向点 \\(j\\) 连边。

形象化的,我们将时间作为纵轴,房屋作为横轴,发现一次治疗就是一条平行于横轴的路径,而两次治疗之间的衔接恰好是一条斜率为 \\(1\\)\\(-1\\) 的直线。所以最短路就是答案。

放一张图便于理解。

直接建图跑最短路是 \\(\\mathcal{O}(N^2)\\) 的,考虑优化建图。

我们对所有点按 \\(T_i\\) 排序,那么当 \\(T_i\\ge T_j\\) 时,有 \\(R_i-T_i+1\\ge L_j-T_j\\) ,否则是 \\(R_i+T_i+1\\ge L_j+T_j\\)

式子一边只与 \\(i,j\\) 中的一个有关,可以看作点的属性,那么我们只需要再满足 \\(T_i\\)\\(T_j\\) 的大小关系。

直接线段树优化建图。线段树优化的最短路有一个优美的性质就是线段树上的边只会转移一次,所以时间复杂度是 \\(\\mathcal{O}(N\\log N)\\)

关于这个性质可以参考这道经典题,也是线段树优化最短路。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
const int inf = 0x7fffffff;
using namespace std;
int n,m,v[N];long long d[N];
struct node{
	int t,l,r,c;
	bool operator<(const node o)const{return t<o.t;}
}u[N];
struct Node{
	int l,r,mna,mnb;
}a[N<<2];
#define L a[x].l
#define R a[x].r
#define ls (x<<1)
#define rs (ls|1)
#define A a[x].mna
#define B a[x].mnb
void updata(int x){
	A=min(a[ls].mna,a[rs].mna);
	B=min(a[ls].mnb,a[rs].mnb);
}
void build(int x,int l,int r){
	L=l,R=r;
	if(l==r){
		A=u[l].l-u[l].t;
		B=u[l].l+u[l].t;
		//cout<<"build  "<<l<<" "<<A<<" "<<B<<endl;
	}
	else{
		int mid=(l+r)>>1;
		build(ls,l,mid);
		build(rs,mid+1,r);
		updata(x);
	}
}
priority_queue<pair<long long,int> >q;
void solve(int x,int l,int r,int val,long long ds){
	//cout<<"AA "<<x<<" "<<l<<" "<<r<<" "<<val<<" "<<ds<<" "<<L<<" "<<R<<" "<<A<<endl;
	if(A>val)return;
	if(L==R){
		if(ds+u[L].c<d[L])d[L]=ds+u[L].c,q.push(make_pair(-d[L],L));
		A=B=inf;return;
	}
	if(L>=l&&R<=r){
		solve(ls,l,r,val,ds);
		solve(rs,l,r,val,ds);
		updata(x);return;
	}
	int mid=(L+R)>>1;
	if(mid>=l)solve(ls,l,r,val,ds);
	if(mid<r)solve(rs,l,r,val,ds);
	updata(x);
}
void calc(int x,int l,int r,int val,long long ds){
	//cout<<"BB "<<x<<" "<<l<<" "<<r<<" "<<val<<" "<<ds<<" "<<L<<" "<<R<<" "<<B<<endl;
	if(B>val)return;
	if(L==R){
		//cout<<"OK "<<l<<" "<<ds<<" "<<u[l].c<<" "<<d[l]<<endl;
		if(ds+u[L].c<d[L])d[L]=ds+u[L].c,q.push(make_pair(-d[L],L));
		A=B=inf;return;
	}
	if(L>=l&&R<=r){
		calc(ls,l,r,val,ds);
		calc(rs,l,r,val,ds);
		updata(x);return;
	}
	int mid=(L+R)>>1;
	if(mid>=l)calc(ls,l,r,val,ds);
	if(mid<r)calc(rs,l,r,val,ds);
	updata(x);
}
void dij(){
	rep(i,1,m)if(u[i].l==1)d[i]=u[i].c,q.push(make_pair(-d[i],i));else d[i]=0x7fffffffffffffffLL;
	//cout<<a[1].mna<<" "<<a[1].mnb<<endl;
	while(!q.empty()){
		int x=q.top().second;q.pop();v[x]=1;
		//cout<<"ss "<<x<<" "<<d[x]<<" "<<u[x].r-u[x].t+1<<" "<<u[x].r+u[x].t+1<<endl;
		if(u[x].r==n){printf("%lld\\n",d[x]);return;}
		solve(1,1,x,u[x].r-u[x].t+1,d[x]);calc(1,x,m,u[x].r+u[x].t+1,d[x]);
		while(!q.empty()&&v[q.top().second])q.pop();
	}
	puts("-1");
}
int main(){
	scanf("%d%d",&n,&m);
	rep(i,1,m)scanf("%d%d%d%d",&u[i].t,&u[i].l,&u[i].r,&u[i].c);
	sort(u+1,u+m+1);
	//rep(i,1,m)printf("cc  %d %d %d %d\\n",u[i].t,u[i].l,u[i].r,u[i].c);
	build(1,1,m);dij();
	return 0;
} 

以上是关于题解[JOISC2020] 治療計画的主要内容,如果未能解决你的问题,请参考以下文章

LOJ 3276 JOISC 2020 Day2 遗迹 题解 (计数DP)

JOISC 2017 题解

「JOISC 2017 Day 3」自然公园 题解

JOISC2020 自闭记

「JOISC 2020」有趣的 Joitter 交友

「JOISC 2020」有趣的 Joitter 交友