空间宝石「最短路+线段树」

空间宝石「最短路+线段树」

题目描述

zP1nG很清楚自己打不过灭霸,所以只能在自己出的题里欺负他。

咳咳。这一次他得到了空间宝石Tesseract。

世界之树连接着九界,此时灭霸和zP1nG都在九界的某个地方。而九界是相互无法到达的。zP1nG为了追杀灭霸,决定使用空间宝石的力量到达灭霸身边。因为zP1nG不擅长使用空间宝石,无法直接开一条从自己的位置到灭霸的位置的传送门,所以他只能无意识地使用空间宝石的力量。zP1nG想知道,在自己胡乱地使用空间宝石后,通过传送门到达灭霸的位置最少需要多长时间。

具体地,九界的编号为 \(0\)~\(8\),共有 \(n\) 道传送门,第 \(i\) 道传送门为优先级为 \(p_i\),由 \(u_i\)\(v_i\),需要花费 \(w_i\) 个时间的单向门。传送的规则为:zP1nG按顺序穿过的传送门的优先级必须是单调不降的。例如,zP1nG穿过了三道传送门到达灭霸的位置,这三道传送门的优先级分别为 \(1→2→2\) 即为一种合法的方式,而优先级分别为 \(1→2→1\) 是不合法的。

zP1nG会使用 \(q\) 次宝石的力量来尝试进行传送:其中第 \(i\) 次尝试会打开数道传送门,这些传送门的优先级会形成 \(s_i\) 个区间。例如,在某次尝试中zP1nG打开了三个区间 \([1,2],[4,7],[9,10]\),那么优先级在这三个区间内的传送门将全部被打开并允许zP1nG穿过。你需要告诉zP1nG在此情况下从自己的位置 \(z_i\) 到达灭霸所在处 \(t_i\) 所需的最短时间。尝试结束后所有传送门会关闭。

输入格式

\(1\) 行包含两个正整数 \(n\)\(S\)\(S\)的含义见数据范围与约定所述。

\(2\)\(n+1\) 行每行包含 \(4\) 个正整数 \(p_i,u_i,v_i,w_i\)

\(n+2\) 行包含一个正整数 \(q\)

\(n+3\) 至第 \(n+q+2\) 行每行若干个正整数,其中前三个数为 \(z_i,t_i,s_i\),之后为\(2*s_i\) 个正整数,表示每个区间的左右端点。

各变量具体含义见题目描述所述。

输出格式

对于zP1nG进行的每次尝试,输出一行一个数表示从zP1nG的位置到灭霸的位置所需的最短时间,如果zP1nG无法到达灭霸的位置则输出 \(-1\)

样例

样例输入

6 2
1 2 4 1
2 1 3 3
3 1 2 2
4 3 4 5
5 2 4 3
6 1 4 2
4
1 4 1 1 3
1 4 1 1 4
1 4 2 5 5 2 3
1 4 1 1 6

样例输出

-1
8
5
2

数据范围与提示

对于100%的数据,\(1≤n≤100,000,1≤S≤5,1≤pi≤2,000,ui≠vi,zi≠ti,1≤wi≤100,000,000,1≤∑si≤10,000\)

思路分析

这题够毒瘤的

  • 首先第一眼看上去应该是让我们求一个带有奇奇怪怪限制的最短路,再进一步看,发现这题只有9个节点???暴力蠢蠢欲动然后上去糊了一个二维spfa完美死掉,我太菜了
  • 只有九个节点,这时侯肯定是邻接矩阵存边最方便了,而相应的 \(Floyd\) 也就呼之欲出。不同优先级的边可以存在不同的矩阵里
  • 那么关键问题来了,不同优先级要怎么合并在一起?其实只要将两个矩阵一起再跑一遍 \(Floyd\) 就可以,联想到矩阵乘,稍微改成 \(Floyd\) 的形式就行了(因为这两个形式差不多)
  • 到这里还并不是最优解,由于我们每次打开的传送门是一个区间,这当然是线段树最擅长的,通过线段树合并即可

详见代码

\(Code\)

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
#include<queue>
#define N 2020
#define R register
using namespace std;
inline int read(){
	int x=0,f=1;
	char ch = getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int inf = 0x3f3f3f3f;
int n,S,q,Max = -1;
struct Martix{ //邻接矩阵来存不同优先级
	int a[10][10];
	Martix(){ //初始化
		memset(a,0x3f,sizeof(a));
		for(int i = 0;i < 9;i++)a[i][i] = 0;
	}
	Martix operator*(const Martix &r)const{ //魔改一下矩阵乘
		Martix res;
		for(int k = 0;k < 9;k++){
			for(int i = 0;i < 9;i++){
				for(int j = 0;j < 9;j++)
					res.a[i][j] = min(res.a[i][j],a[i][k]+r.a[k][j]);
			}
		}
		return res;
	}
}a[N],tr[N<<2];
#define ls u<<1
#define rs u<<1|1
void build(int u,int l,int r){ //线段树进行合并
	if(l==r){tr[u] = a[l];return;}
	int mid = (l+r)>>1;
	build(ls,l,mid);
	build(rs,mid+1,r);
	tr[u] = tr[ls]*tr[rs]; //注意是左右儿子放在一起跑一遍Floyd
}
Martix query(int u,int l,int r,int s,int t){
	if(s<=l&&t>=r)return tr[u];
	int mid = (l+r)>>1;
	if(t<=mid)return query(ls,l,mid,s,t);
	if(s>mid)return query(rs,mid+1,r,s,t);
	return query(ls,l,mid,s,t)*query(rs,mid+1,r,s,t);
}
struct node{ //结构体放在优先队列里用,根据优先级,即左端点小的优先
	int l, r;
	node(){}
	node(int _l,int _r){
		l = _l,r = _r;
	}
	bool operator <(const node &a)const{
		return l > a.l;
	}
};
int main(){
	n = read(),S = read();
	for(int i = 1;i <= n;i++){
		int p,u,v,w;
		p = read(),u = read(),v = read(),w = read();
		if(a[p].a[u][v]>=w)a[p].a[u][v] = w;//处理重边
		Max = max(Max,p); //处理出线段树的右端点
	}
	for(int th  = 1;th <= Max;th++){ //两个矩阵放一起之前先求出同一矩阵的最短路
		for(int k = 0;k < 9;k++){
			for(int i = 0;i < 9;i++){
				for(int j = 0;j < 9;j++){
					a[th].a[i][j] = min(a[th].a[i][j],a[th].a[i][k]+a[th].a[k][j]);
				}
			}
		}
	}
	build(1,1,Max);
	q = read();
	for(int i = 1;i <= q;i++){
		int z,t,s;z = read(),t = read(),s = read();
		priority_queue<node>q;
		for(int j = 1;j <= s;j++){
			int l,r;l = read(),r = read();
			q.push(node(l,r));
		}
		Martix ans;
		for(int j = 1;j <= s;j++){
			node p = q.top();q.pop();
			int l = p.l,r = p.r;
			ans = ans*query(1,1,Max,l,r); //每次询问乘上就好
		}
		if(ans.a[z][t]<inf)printf("%d\n",ans.a[z][t]);
		else puts("-1");
	}
	return 0;
}
posted @ 2020-08-05 06:56  HH_Halo  阅读(165)  评论(0编辑  收藏  举报