空间宝石「最短路+线段树」
作者:互联网
空间宝石「最短路+线段树」
题目描述
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;
}
标签:include,优先级,宝石,int,线段,zP1nG,传送门,灭霸,短路 来源: https://www.cnblogs.com/hhhhalo/p/13437487.html