【玛丽卡】【题解】
作者:互联网
题目
给定N个点,M条边的无向图,N<=1000,M<=N*(N-1)/2。求1N的最短路中把任意一条边删去后,1N新的最短路长度的最大值。
Solution
一种暴力是先把最短路求出后,枚举每条边,将其删去,再重新跑最短路,用不加优化dij,复杂度O(N^3)
发现很难删去一条边,并很快的维护出新的最短路。
考虑枚举新的最短路的边,并寻找原最短路上有哪些边可以被其替代。
发现可以被替代的边一定是是最短路上连续的一段,因此可以用线段树维护区间取min。
如何求解新最短路的长度?
该边的长度+从起点到某端点的长度+从终点到另一端点的长度。后两个可以通过两遍dij求出。
注意实际枚举的是有向边,有可能会出现新最短路对应原最短路上为一个点的情况,此时要特判。
如何求线段树维护的区间端点?
先将原最短路的边编号,对于每个最短路上的点维护向1和向n的边的编号。
而且求解最短路的过程构成一个树,表示子节点的值又父节点转移过来。
记录每的点的fa,对于枚举的边的端点,一直跳fa,直到为最短路上的点,这个过程可以用并查集优化,预先把最短路上的fa设为自己,其余路径压缩。
Code
#include<bits/stdc++.h>
using namespace std;
//#define int long long
inline int read()
{
int x=0,w=1;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if(ch=='-') {w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
return x*w;
}
inline void write(int x)
{
if(x<0) putchar('-'),x=~(x-1);
if(x>9) write(x/10);
putchar('0'+x%10);
}
const int N=1010,inf=0x3f3f3f3f;
int n,a[N][N],d[2][N],fa[2][N],id[2][N],vis[N],v[N][N];
struct node{
int l,r,minn,tag;
}t[N<<2];
int s,m;
void dij(int p)
{
d[p][s]=0;
fa[p][s]=s;
memset(vis,0,sizeof vis);
for(int i=1;i<n;++i)
{
int x=0;
for(int j=1;j<=n;++j){
if(vis[j]) continue;
if(x==0||d[p][j]<d[p][x]) x=j;
}
vis[x]=1;
for(int j=1;j<=n;++j) {
if(d[p][j]>d[p][x]+a[x][j]) d[p][j]=d[p][x]+a[x][j],fa[p][j]=x;
}
}
}
int find(int p,int x)
{
return fa[p][x]==x?x:fa[p][x]=find(p,fa[p][x]);
}
void build(int x,int l,int r)
{
t[x].l=l,t[x].r=r,t[x].minn=t[x].tag=inf;
if(l==r) return ;
int mid=l+r>>1;
build(x<<1,l,mid);build(x<<1|1,mid+1,r);
}
void pushdown(int x)
{
if(t[x].tag!=inf)
{
int c=t[x].tag;t[x].tag=inf;
t[x<<1].tag=min(t[x<<1].tag,c);
t[x<<1|1].tag=min(t[x<<1|1].tag,c);
t[x<<1].minn=min(t[x<<1].minn,c);
t[x<<1|1].minn=min(t[x<<1|1].minn,c);
}
}
void change(int x,int l,int r,int c)
{
t[x].minn=min(t[x].minn,c);
if(t[x].l>=l&&t[x].r<=r){
t[x].tag=min(t[x].tag,c);
return;
}
pushdown(x);
int mid=t[x].l+t[x].r>>1;
if(l<=mid) change(x<<1,l,r,c);
if(r>mid) change(x<<1|1,l,r,c);
}
int query(int x,int pos)
{
if(t[x].l==t[x].r) return t[x].minn;
pushdown(x);
int mid=t[x].l+t[x].r>>1;
if(pos<=mid) return query(x<<1,pos);
return query(x<<1|1,pos);
}
signed main()
{
n=read();m=read();
memset(d,0x3f,sizeof d);memset(a,0x3f,sizeof a);
for(int i=1;i<=m;++i)
{
int x=read(),y=read(),z=read();
a[x][y]=a[y][x]=min(a[x][y],z);
}
s=1;dij(0);
s=n;dij(1);
int cnt=0;
for(int i=n;fa[0][i]!=i;i=fa[0][i])
{
id[1][i]=id[0][fa[0][i]]=++cnt;
fa[1][fa[0][i]]=fa[0][i];
v[i][fa[0][i]]=v[fa[0][i]][i]=1;
}
build(1,1,cnt);
for(int i=1;i<=n;++i) if(fa[1][i]==i) fa[0][i]=i;
for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
if(i==j||v[i][j]||a[i][j]==a[0][0]) continue;
int u=find(0,i),v=find(1,j);
if(id[1][v]&&id[1][v]<=id[0][u])
change(1,id[1][v],id[0][u],a[i][j]+d[0][i]+d[1][j]);
}
}
int ans=0;
for(int i=1;i<=cnt;++i) ans=max(ans,query(1,i));
write(ans);
return 0;
}
/*
5 7
1 2 8
1 4 10
2 3 9
2 4 10
2 5 1
3 4 7
3 5 10
*/
/*
5 7
1 2 8
1 5 3
1 4 5
2 3 2
2 4 3
2 5 9
3 4 7
*/
标签:ch,路上,int,题解,短路,玛丽,fa,枚举 来源: https://www.cnblogs.com/glq-Blog/p/16125237.html