【CodeVS1021】玛丽卡 最短路+线段树

题目链接
声明:感谢fzw(kryj)的创意和热心讲解

题目描述

麦克找了个新女朋友,玛丽卡对他非常恼火并伺机报复。
因为她和他们不住在同一个城市,因此她开始准备她的长途旅行。
在这个国家中每两个城市之间最多只有一条路相通,并且我们知道从一个城市到另一个城市路上所需花费的时间。
麦克在车中无意中听到有一条路正在维修,并且那儿正堵车,但没听清楚到底是哪一条路。无论哪一条路正在维修,从玛丽卡所在的城市都能到达麦克所在的城市。
玛丽卡将只从不堵车的路上通过,并且她将按最短路线行车。麦克希望知道在最糟糕的情况下玛丽卡到达他所在的城市需要多长时间,这样他就能保证他的女朋友离开该城市足够远。
编写程序,帮助麦克找出玛丽卡按最短路线通过不堵车道路到达他所在城市所需的最长时间(用分钟表示)。
城市数 N 1000 麦克在1,玛丽卡在N

吐槽

几乎所有网上能找到的题解都是NM的,好郁闷。。
具体地说,他们都是在当前最短路上枚举删边,重新跑SPFA或者Dijkstra
这种做法的时间复杂度很好分析。原最短路上的边数为O(N)级别,每一次最短路至少要遍历所有边,所以是O(NM)。当 M N 2 ,dijkstra算法复杂度可以看做 O ( N 2 ) ,SPFA算法则更慢。因此网上n次SPFA的代码显然应该 O ( N 3 ) T到不知哪里去了
但是CodeVS的数据好像根本没卡?没关系我有一数据生成器请诸位享用

#include<cstdio>
int n=1000;
int m=n*(n-1)/2;
int main()
{
    printf("%d %d\n",n,m);
    for(int i=1;i<n;i++)
    {
        printf("%d %d 1\n",i,i+1);
        for(int j=i+2;j<=n;j++)
            printf("%d %d 1000\n",i,j);
    }
}

实测黄学长的代码跑了32秒。。。

题解

我们考虑删掉原最短路上的一条边之后的图
删边后,有些点到起点s的最短路ds不变,我们设这个点集为L;有些点到终点t的最短路dt不变,我们设这个点集为R
每个点至少在L和R的一个中,因为假设点p不在任何一个,说明p到s和t的最短路都经过被删的边。假设被删的边是(u->v),边长为w,那么路径u-v-p比路径u-p(不经过v,以下同理)短,v-u-p比v-p短,这样的话,d(u-p)>d(v-p)+w且d(v-p)>d(u-p)+w,显然是矛盾的
因而新的最短路一定是先在L中走,经过一条边,进入R集合,最后到达t。
我们可以枚举这条中间的边,就能找到所有“可能的”新最短路长度。
但是怎么才能知道某个边带来的路径会不会受堵车的边的影响呢?
从s到t的路径一定可以从原最短路上的某个点脱离,再从某个点返回到原最短路
我们使得保证s-u和v-t长度不变(即保持L,R集合的性质)的情况下,尽量地早脱离,晚返回,这样就能使得脱离点和返回点之间的所有边都无法对其产生影响。实现前一点我们可以通过在原最短路上按顺序从每一个点开始,只走 d s u + w = d s v 的边看可以到达哪些点,把这些点的最早脱离位置都保存起来。后一点也同样,只是反过来。
在枚举一条(u,v,w)的边时,如果可以从l点脱离到达u,再从v点出发在r点返回,那么删去l到r之间的任意一条边,都仍存在一条长度为 d s u + w + d t v 的路径可以从s到达t,最后我们要求的是删去每个边的情况中,新最短路的最长值,相当于一个序列,支持区间内所有数和另一个数取min,最后要查询整个序列的最大值,线段树可以解决这个问题

流程简述及时间复杂度分析

  1. 求原图最短路并标记上面的点和边,并按序编号—— O ( M + N l o g N )
  2. 求每一个点i想在L集合中,最早的脱离位置 l i (上一条的编号),dfs实现,每个点最多被递归进入一次,从这个点出发的边也只能被走一次—— O ( M )
  3. 求每个点想在R集合中的最晚返回位置 r i ,同上
  4. 枚举每条边,给删去原最短路上 [ l i + 1 , r i ] 中边的情况增加一种可能路径——O(MlogN)
  5. 线段树整体求最大值——O(logN)

代码

很长,但是我严谨我自豪

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 1004
#define inf 2147483647
int n,m,c;
struct edge
{
    int v,w,next;
    edge(int v,int w,int n):v(v),w(w),next(n){}
    edge(){}
}e[maxn*maxn];
int ind[maxn];
int newedge=1;
void addedge(int l,int v,int u)
{
    e[++newedge]=edge(v,l,ind[u]);
    ind[u]=newedge;
    e[++newedge]=edge(u,l,ind[v]);
    ind[v]=newedge;
}
int read()
{
    int d=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') 
        ch=getchar();
    while(ch>='0'&&ch<='9')
    {
        d=d*10+ch-'0';
        ch=getchar();
    }
    return d;
}
struct node
{
    int id;
    int d;
    node(int id,int d):id(id),d(d){}
};
bool operator <(node a,node b)
{
    return a.d>b.d;
}
int ds[maxn],dt[maxn];
int dep[maxn];
int f[maxn];
bool vzt[maxn];
priority_queue<node> prq;
void dijkstra(int s,int *d)
{
    for(int i=1;i<=n;i++)
        d[i]=inf;
    d[s]=dep[s]=0;
    prq.push(node(s,0));
    while(!prq.empty())
    {
        node x=prq.top();prq.pop();
        while(d[x.id]!=x.d)
        {
            if(prq.empty()) return;
            x=prq.top();
            prq.pop();
        }
        int u=x.id;
        for(int j=ind[u];j;j=e[j].next)
        {
            int v=e[j].v;
            if(d[v]>d[u]+e[j].w)
            {
                d[v]=d[u]+e[j].w;
                f[v]=j;
                dep[v]=dep[u]+1;
                prq.push(node(v,d[v]));
            }
        }
    }
}
bool dtag[maxn];
bool btag[maxn*maxn];
int a[maxn];
int l[maxn],r[maxn];
void dfs(int u,int *a,int *d)
{
    vzt[u]=true;
    for(int i=ind[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(d[v]!=d[u]+e[i].w||vzt[v]||dtag[v]) continue;
        a[v]=a[u];
        dfs(v,a,d);
    }
}
namespace segtree
{
    int tag[maxn<<2];
    void puttag(int t,int v)
    {
        if(!tag[t]||tag[t]>v)
            tag[t]=v;
    }
    void pushdown(int t)
    {
        if(tag[t])
        {
            puttag(t*2,tag[t]);
            puttag(t*2+1,tag[t]);
            tag[t]=0;
        }
    }
    void set(int ql,int qr,int v,int t=1,int l=1,int r=c)
    {
        if(ql<=l&&r<=qr)
        {
            puttag(t,v);
            return;
        }
        pushdown(t);
        int mid=(l+r)>>1;
        if(mid>=ql) set(ql,qr,v,t*2,l,mid);
        if(mid+1<=qr) set(ql,qr,v,t*2+1,mid+1,r);
    }
    int getmax(int t=1,int l=1,int r=c)
    {
        if(l==r) return tag[t];
        pushdown(t);
        int mid=(l+r)>>1;
        return max(getmax(t*2,l,mid),getmax(t*2+1,mid+1,r));
    }
}
int main()
{
    freopen("marica.in","r",stdin);
    n=read();
    m=read();
    for(int i=1;i<=m;i++)
        addedge(read(),read(),read());
    dijkstra(n,dt);
    dijkstra(1,ds);
    dtag[1]=true;
    a[0]=1;
    for(int i=n;i!=1;i=e[f[i]^1].v)
    {
        dtag[i]=true;
        a[dep[i]]=i;
        btag[f[i]]=btag[f[i]^1]=true;
    }
    c=dep[n];
    for(int i=0;i<=c;i++)
    {
        l[a[i]]=i;
        dfs(a[i],l,ds);
    }
    memset(vzt,0,sizeof(vzt));
    for(int i=c;i>=0;i--)
    {
        r[a[i]]=i;
        dfs(a[i],r,dt);
    }
    for(int i=2;i<=newedge;i++)
    {
        if(btag[i]) continue;
        int u=e[i^1].v,v=e[i].v;
        if(l[u]>=r[v]) continue;
        int ans=ds[u]+dt[v]+e[i].w;
        segtree::set(l[u]+1,r[v],ans);
    }
    printf("%d",segtree::getmax());
}

猜你喜欢

转载自blog.csdn.net/leo_h1104/article/details/80719247