一场比赛两个线段树分治😅
大概看了一下,题解的做法大多都比较脑洞😅
显然想到枚举不在环上的这个点,将其删去过后找最小环即可。因为是稠密图所以想到用 Floyd \text{Floyd} Floyd。这样我们得到了 O ( n 4 ) O(n^4) O(n4)的做法。
因为要删除一个点比较慢,所以考虑优化这个过程。我们发现 i i i对 [ 1 , i − 1 ] [1,i-1] [1,i−1]和 [ i + 1 , n ] [i+1,n] [i+1,n]有贡献,这不就是一个节点出现的区间吗?考虑用 线段树分治 来优化它。这也能线段树分治?
发现插入一个点其实就是 Floyd \text{Floyd} Floyd的实现方式,因此直接做就完了😅
这里记住一个结论,用 Floyd \text{Floyd} Floyd维护两点间最短路,插入一个点是 O ( n 2 ) O(n^2) O(n2)的。
但是统计环的时候就会有问题。传统的 Floyd \text{Floyd} Floyd找环的时候是枚举环上一个点 k k k以及环上与 k k k 相邻 的两个点 i , j i,j i,j,然后用 dist(i,j) + w ( i , k ) + w ( k , j ) \text{dist(i,j)}+\text{w}(i,k)+\text{w}(k,j) dist(i,j)+w(i,k)+w(k,j)更新答案。并且因为 k k k是环上编号最大的点所以不重不漏。注意其本质也是将 k k k删去过后求 i i i到 j j j的最短路径,所以发现我们把删去的点的定义换成环上度数为 3 3 3的点这个问题就可以解决了,因为与其相连的环外的点可以直接得到。
其实可以考虑优化一下空间。发现每次线段树分治遍历的都是一条链,所以直接用深度来存,这样空间复杂度就是 O ( n 2 log n ) O(n^2\log n) O(n2logn)的。虽然 atcoder \text{atcoder} atcoder不卡空间。
复杂度 O ( n 3 log n ) O(n^3\log n) O(n3logn)。
cpp
#include<bits/stdc++.h>
#define fi first
#define se second
#define ll long long
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
using namespace std;
const int N=305;
int n,m;
int vs[N],f[10][N][N],now[N][N],w[N][N],to[N][3],res=inf;
vector<int>nums[N<<2];
void ins(int p,int l,int r,int ql,int qr,int x){
if(ql>qr)return;
if(ql<=l&&r<=qr){
nums[p].pb(x);
return;
}
int mid=l+r>>1;
if(ql<=mid)ins(p<<1,l,mid,ql,qr,x);
if(mid<qr)ins(p<<1|1,mid+1,r,ql,qr,x);
}
void solve(int p,int l,int r,int d){
for(int i=1;i<=n;i++)memcpy(f[d][i],now[i],sizeof now[i]);
for(auto x:nums[p]){
vs[x]=1;
for(int i=1;i<=n;i++){
if(vs[i])now[x][i]=now[i][x]=w[i][x];
}
for(int i=1;i<=n;i++){
if(i==x||!vs[i])continue;
for(int j=1;j<=n;j++){
if(j==x||!vs[j])continue;
now[x][j]=now[j][x]=min(now[x][j],now[x][i]+now[i][j]);
}
}
for(int i=1;i<=n;i++){
if(i==x||!vs[i])continue;
for(int j=1;j<=n;j++){
if(j==x||!vs[j])continue;
now[i][j]=min(now[i][j],now[i][x]+now[x][j]);
}
}
}
if(l!=r){
int mid=l+r>>1;
solve(p<<1,l,mid,d+1),solve(p<<1|1,mid+1,r,d+1);
}
else if(to[l][2]!=inf){
for(int i=1;i<=n;i++){
if(!vs[i])continue;
for(int j=1;j<=n;j++){
if(i==j||!vs[j])continue;
int p;
if(to[l][0]!=i&&to[l][0]!=j)p=to[l][0];
else if(to[l][1]!=i&&to[l][1]!=j)p=to[l][1];
else p=to[l][2];
if(w[l][i]!=inf&&w[l][j]!=inf&&now[i][j]!=inf)res=min(res,w[l][i]+w[l][j]+now[i][j]+w[l][p]);
}
}
}
for(int i=1;i<=n;i++)memcpy(now[i],f[d][i],sizeof f[d][i]);
for(auto x:nums[p])vs[x]=0;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;for(int i=1;i<=n;i++)ins(1,1,n,1,i-1,i),ins(1,1,n,i+1,n,i);
memset(w,0x3f,sizeof w),memset(now,0x3f,sizeof now);for(int i=1;i<=m;i++){
int x,y,z;cin>>x>>y>>z;w[x][y]=w[y][x]=z;
}for(int i=1;i<=n;i++)w[i][i]=now[i][i]=0;
for(int i=1;i<=n;i++){
to[i][0]=to[i][1]=to[i][2]=inf;
for(int j=1;j<=n;j++){
if(i==j)continue;
if(to[i][0]==inf||w[i][j]<w[i][to[i][0]])to[i][2]=to[i][1],to[i][1]=to[i][0],to[i][0]=j;
else if(to[i][1]==inf||w[i][j]<w[i][to[i][1]])to[i][2]=to[i][1],to[i][1]=j;
else if(to[i][2]==inf||w[i][j]<w[i][to[i][2]])to[i][2]=j;
}
}
solve(1,1,n,0);cout<<(res==inf?-1:res);
}