【学习笔记】[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);
}
相关推荐
搬砖的小码农_Sky2 小时前
C语言:数组
c语言·数据结构
Swift社区3 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman4 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
先鱼鲨生4 小时前
数据结构——栈、队列
数据结构
一念之坤4 小时前
零基础学Python之数据结构 -- 01篇
数据结构·python
IT 青年4 小时前
数据结构 (1)基本概念和术语
数据结构·算法
熬夜学编程的小王4 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
Dong雨5 小时前
力扣hot100-->栈/单调栈
算法·leetcode·职场和发展
SoraLuna5 小时前
「Mac玩转仓颉内测版24」基础篇4 - 浮点类型详解
开发语言·算法·macos·cangjie
liujjjiyun5 小时前
小R的随机播放顺序
数据结构·c++·算法