【学习笔记】[ABC308Ex] Make Q

一场比赛两个线段树分治😅

大概看了一下,题解的做法大多都比较脑洞😅

显然想到枚举不在环上的这个点,将其删去过后找最小环即可。因为是稠密图所以想到用 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);
}
相关推荐
迷途之人不知返7 小时前
数据结构之,栈与队列
数据结构
MATLAB代码顾问8 小时前
多种时间序列预测算法的MATLAB实现
开发语言·算法·matlab
高山上有一只小老虎9 小时前
字符串字符匹配
java·算法
愚润求学9 小时前
【动态规划】专题完结,题单汇总
算法·leetcode·动态规划
MOONICK9 小时前
数据结构——哈希表
数据结构·哈希算法·散列表
林太白10 小时前
跟着TRAE SOLO学习两大搜索
前端·算法
ghie909010 小时前
图像去雾算法详解与MATLAB实现
开发语言·算法·matlab
云泽80810 小时前
从三路快排到内省排序:探索工业级排序算法的演进
算法·排序算法
weixin_4684668511 小时前
遗传算法求解TSP旅行商问题python代码实战
python·算法·算法优化·遗传算法·旅行商问题·智能优化·np问题
FMRbpm11 小时前
链表5--------删除
数据结构·c++·算法·链表·新手入门