[10.12模拟赛] 旅程 (最短路)

[10.12模拟赛] 旅程

题目描述

神即将带领一些人去他们的孤寂之境,由于这个世界的不稳定,地点之间的有向道路会不定期地毁坏,出于工作准备,神想知道在某些道路毁坏之后某两点之间的最短路。
就是给定一个有向图,现有两个操作,操作 1 是删除一条边(一条边可重复删除),操作 2是询问两个点之间的最短路。

输入

第 1 行两个正整数 n, m,分别表示图的点数和操作数。
第 2 行至第 n + 1 行每行 n 个正整数,为图的邻接矩阵,第 i 行第 j 列的数表示点 i 和点j 间距离,保证对角线为 0。
接下来 m 行每行三个正整数 c, x, y,c 表示操作种类,为 1 或 2,当 c = 1 时表示删除 x与 y 相连的边,当 c = 2 时表示询问 x 到 y 的最短路,若不可达则输出 −1。

输出

输出若干行,每个 2 操作对应一行,答案为询问中 x 到 y 的最短路或 −1

样例输入

5 6
0 6 6 10 10
2 0 7 8 6
10 5 0 10 3
9 5 8 0 7
4 9 8 3 0
1 2 3
1 4 1
2 1 3
1 4 2
1 1 2
2 4 1

样例输出

6
11

提示

对于 30% 的数据:n, m ≤ 10
对于 50% 的数据:n, m ≤ 50
对于 100% 的数据:n ≤ 200, m ≤ 100000, 操作 1 不超过 200 次,边权不超过 10000

Solution

首先对于任意两点最短路,我们可以首先(O(n^3))预处理,然后(O(1))查询
但是删除边怎么办? 我们不可能在题目要求的复杂度下动态维护删边操作,那只能换一种思路了

倒着来做,我们首先预处理出来是在所有删边操作全部结束后的任意两点最短路,那么每次对于一个删边操作,实际上变成了加边,我们要用加的这条边去更新其他点,因为已经确定是由新加进来的边去更新其他点,所以我们可以省去floyed枚举断电那一重循环,(O(n^2))即可,在加上删边操作最多200个,总复杂度(O(n^3))

注意:有坑点,题目说可以重复删边,所以我们要记录一下一条边是什么时候最早被删的,只有到那个时候才算真正加上这条边

Code

#include<bits/stdc++.h>
#define rg register
#define il inline
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)
#define lol long long
#define in(i) (i=read())
using namespace std;

const lol N=210,M=1e5+10,inf=1e15;

lol read() {
	lol ans=0,f=1; char i=getchar();
	while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
	while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0',i=getchar();
	return ans*=f;
}
lol n,m;
lol w[N][N],del[N][N],vis[N][N],ans[M];
struct AQ {
    lol op,x,y;
}t[M];
int main()
{
    //freopen("journey.in","r",stdin);
    //freopen("journey.out","w",stdout);
    in(n),in(m);
    memset(w,127,sizeof(w));
    memset(vis,127,sizeof(vis));
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) in(w[i][j]),del[i][j]=w[i][j];
    
    for(int i=1;i<=n;i++) w[i][i]=del[i][i]=0;
    
    for(lol i=1;i<=m;i++) {
        in(t[i].op),in(t[i].x),in(t[i].y);
        if(t[i].op==1) {
            del[t[i].x][t[i].y]=inf;
            vis[t[i].x][t[i].y]=min(vis[t[i].x][t[i].y],i);//第一次删边
        }
    }
    
    for(lol k=1;k<=n;k++) 
        for(lol i=1;i<=n;i++) 
            for(lol j=1;j<=n;j++) {
                if(i==k || j==k || i==j) continue;
                del[i][j]=min(del[i][j],del[i][k]+del[k][j]);
            }

    
    for(lol i=m;i>=1;i--) {
        if(t[i].op==2) ans[i]=del[t[i].x][t[i].y];
        else {
            if(vis[t[i].x][t[i].y]!=i) continue;
            
            del[t[i].x][t[i].y]=min(del[t[i].x][t[i].y],w[t[i].x][t[i].y]);//原边权与已经跑出来的最短路取个最小值
            for(lol p=1;p<=n;p++) {
                for(lol q=1;q<=n;q++) {
                    del[p][q]=min(del[p][q],del[p][t[i].x]+del[t[i].y][q]+del[t[i].x][t[i].y]);
                }
            }
        }
    }
    for(lol i=1;i<=m;i++) {
        if(t[i].op==2) printf("%lld
",ans[i]);
    }
}
原文地址:https://www.cnblogs.com/real-l/p/9778628.html