LA 4080 (多源最短路径+边修改+最短路径树)

题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=32266

题目大意:①先求任意两点间的最短路径累加和,其中不连通的边权为L   ②删除任意一条边,求全局最短路径和的最大值。

解题思路

首先说下多源最短路中,floyd和和优先队列优化的dijkstra的取舍。floyd比较好拍,dijkstra具有常数有势,以及灵活性(如求第二问的时候)。

本题最烦人的是枚举删除一条边,按照正常思维,要重新做n次dijkstra,复杂度已经到了可怕的(n*m^2*logn),那么是否有必要每次修改一条边的时候,全源重新做一次最短路呢?

答案是否定的。只要构建一颗最短路径树即可。

不要被名字唬住,其实就是一个二维数组,belong[边id][s点],即初次做全源Dijkstra的时候,为每条边进行标记,标记内容为本次dij的s点。

注意这里的边指的是输入边id,不是图中的边(这题是无向图,输入边被add了两次)。

枚举删除边时,如果belong[边id][s点]=true,则说明这条边与这次的单源dij有关,必须重新dij。如果为false,则无关,值还是初次做dij的值。

标记belong的方法:在每次优先队列出队的时候,对出队点所在的入队前的边进行标记,具体方法是开一个p数组,每次Relax的时候,记录一下Relax的边即可。之后入队在取出,p数组就能立刻取出入队前的边了。

本题存在重边,不支持的重边的数据结构注意了,尤其是邻接表。推荐链式前向星。

另外两个问的结果都超过了int32。

#include "cstdio"
#include "queue"
#include "cstring"
using namespace std;
#define maxn 155
#define maxp 2005
#define inf 1<<28
#define LL long long
struct Edge
{
    int next,to,d,id;
}e[maxp*2];
struct status
{
    int d,p;
    status(int d,int p):d(d),p(p) {}
    bool operator < (const status &a) const {return d > a.d;}
};
int n,m,l,tol,head[maxn],d[maxn],p[maxn],dis[maxn][maxn];
LL ans1,ans2,w[maxn];
bool vis[maxn],belong[maxp][maxn],del[maxp];
void addedge(int u,int v,int c,int id)
{
    e[tol].id=id;
    e[tol].d=c;
    e[tol].to=v;
    e[tol].next=head[u];
    head[u]=tol++;
}
void dijkstra1(int s)
{
    memset(vis,false,sizeof(vis));
    memset(p,0,sizeof(p));
    for(int i=1;i<=n;i++) d[i]=(i==s?0:inf);
    priority_queue<status> Q;
    Q.push(status(0,s));
    while(!Q.empty())
    {
        status tt=Q.top();Q.pop();
        int x=tt.p;
        if(vis[x]) continue;
        vis[x]=true;
        belong[p[x]][s]=true;
        for(int i=head[x];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            if(d[x]+e[i].d<d[v])
            {
                p[v]=e[i].id;
                d[v]=d[x]+e[i].d;
                Q.push(status(d[v],v));
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        if(d[i]==inf)
        {
            ans1+=l;
            w[s]+=l;
        }
        else
        {
            ans1+=d[i];
            w[s]+=d[i];
        }
    }
}
LL dijkstra2(int s)
{
    memset(vis,false,sizeof(vis));
    for(int i=1; i<=n; i++) d[i]=(i==s?0:inf);
    priority_queue<status> Q;
    Q.push(status(0,s));
    while(!Q.empty())
    {
        status tt=Q.top();
        Q.pop();
        int x=tt.p;
        if(vis[x]) continue;
        vis[x]=true;
        for(int i=head[x]; i!=-1; i=e[i].next)
        {
            if(del[e[i].id]) continue; //标记为删除的边跳过
            int v=e[i].to;
            if(d[x]+e[i].d<d[v])
            {
                d[v]=d[x]+e[i].d;
                Q.push(status(d[v],v));
            }
        }
    }
    long long tt=0;
    for(int i=1; i<=n; i++)
    {
        if(d[i]==inf) tt+=l;
        else tt+=d[i];
    }
    return tt;
}
int main()
{
    int u,v,c;
    while(scanf("%d%d%d",&n,&m,&l)!=EOF)
    {
        memset(head,-1,sizeof(head));
        memset(belong,false,sizeof(belong));
        memset(del,false,sizeof(del));
        memset(w,0,sizeof(w));
        tol=ans1=ans2=0;
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&v,&c);
            addedge(u,v,c,i);
            addedge(v,u,c,i);
        }
        for(int i=1;i<=n;i++)
            dijkstra1(i);
        for(int i=1;i<=m;i++)
        {
            LL tt=0;
            del[i]=true;
            for(int j=1;j<=n;j++)
                if(belong[i][j]) tt+=dijkstra2(j);
                else tt+=w[j];
            del[i]=false;
            ans2=max(ans2,tt);
        }
        printf("%lld %lld\n",ans1,ans2);
    }
}

2814660

neopenx

UVALive 4080

Accepted

0 KB

409 ms

C++ 4.5.3

2990 B

2014-10-04 18:34:44

时间: 2024-10-18 21:19:40

LA 4080 (多源最短路径+边修改+最短路径树)的相关文章

kali高速更新源以及主题修改方法

文章不小心删了~这是我以前写的文章了了.实用性较强,所以现在补回来! 安装完kali之后,需要对软件进行一次整体更新:apt-get update & apt-get upgrade 但是,先别着急,默认kali安装完之后,里面的更新源很慢,是kali官网的更新源,我们从国内进行下载,肯定会很慢. 所以,我们要添加一些速度快的更新源,以方便我们进行更新: 首先,我们要打开sources.list文件,进行添加更新源:leafpad /etc/apt/sources.list然后选择添加以下较快的

源码中修改Android的开机画面和动画【转】

本文转载自:http://blog.csdn.net/dddxxxx/article/details/54343976 参照文章:http://blog.csdn.net/a345017062/article/details/6222962.http://bbs.gfan.com/android-146253-1-1.html. Android系统开机显示画面分成两个过程,第一个过程从按电源键到Frameworks启动为止.第二个过程从Frameworks启动完成到Launcher程序启动完成.

Android对apk源码的修改--反编译+源码修改+重新打包+签名【附HelloWorld的修改实例】

近期遇到了需要修改apk源码的问题,于是上网查了下相关资料,编写了HelloWorld进行修改看看可行性,经过实验证明此方案可行,并且后来也成功用这个方法对目标apk进行了修改,只不过需要修改的部分比HelloWorld复杂些,但是只要了解下smali也能进行相关的修改,下面讲下具体的步骤,文中所用到的资源会在文章的结尾给出,感兴趣的可以下载试试. 首先介绍下要用到的工具: jdk:这个不用多说了 baksmali:把classes.dex转为为smali文件的工具 dex2jar:classe

少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小

少年,想学带修改主席树吗 | BZOJ1901 带修改区间第k小 有一道题(BZOJ 1901)是这样的:n个数,m个询问,询问有两种:修改某个数/询问区间第k小. 不带修改的区间第k小用主席树很好写,不会的同学可以看一下这个. 加上修改怎么做呢?我们可以用数学老师成天讲的类比思想: 可以发现,不修改的区间k小问题中,每加入一个原序列中的数,对应的主席树在上一个的基础上进行修改,而查询的时候用右端点主席树减去左端点左边的主席树.这样的操作就像是维护前缀和:每次加入一个元素的时候,sum[i] =

[BZOJ 4826]影魔 区间修改主席树 标记永久化

为了这道题还特地去学了标记永久化,可能对于区间修改主席树或者树套树比较有用吧OvO 我们可以把答案分为两部分:p1造成的和p2造成的 我们枚举序列,用单调栈求出序列每一个位置i,左右边第一个比它大的L,R 开三棵主席树tree1 tree2 tree3 把L扔进tree1的R位置(单点+1),L+1~i-1扔进tree2的R位置,i+1~R-1扔进tree3的L位置(区间+1) 然后询问[l,r]的时候,求出三棵区间主席树 p1造成的贡献为区间tree1内大于等于L的个数 p2造成的贡献为区间t

支持区间修改的树状数组

支持区间修改的树状数组 原理 对于一个数组\(a\),以及\(a\)的差分\(c\),显然有\(c[i]=a[i]-a[i-1]\) 那么对于数组a的前缀和有 \(\sum_{i=1}^n{a_i}=c[1]+(c[1]+c[2])+...(c[1]+c[2]+...+c[n])\) 进一步的: \(\sum_{i=1}^n{a_i}=c[1]*n+c[2]*(n-1)+...+c[n]*(n-n+1)\) 展开括号内 \(\sum_{i=1}^n{a_i}=c[1]*n+c[2]*n+...+

单源最短路径问题-无权最短路径算法

无权最短路径算法:借助广度优先搜索,距开始最近的那些顶点首先被赋值,而最远的顶点最后被赋值 伪码如下 void Unweighted(Table T) { Queue Q; Vertext V, W; Q = CreateQueue(NumVertex); MakeEmpty(Q); Enqueue(S, Q); while (!IsEmpty(Q)) { V = Dequeue(Q); T[V].Know = True; for each W adjacent to V if (T[W].Di

Centos7源的配置修改

对yum网络源的讲解 http://www.cnblogs.com/mchina/archive/2013/01/04/2842275.html http://wuyelan.blog.51cto.com/6118147/1546674 修改为国内的aliyun 的源 http://mirrors.aliyun.com/help/centos

Android4.2.2 动态显示隐藏屏幕底部的导航栏(对系统源码进行修改)

需求如题. 在Android4.2.2中,导航栏(也就是屏幕底部的三个按钮,home,back,recentapp)是系统应用SystemUi.apk的一部分,简言之,我们的需求就是让我们的app来控制SystemUi.apk,达到动态显示隐藏屏幕底部导航栏的效果.我们可以在SystemUi.apk的源码中留下接口便于我们控制导航栏的显示和隐藏,我们可以通过广播的接收与发送的方式来实现这个接口. app------->发送广播(hide/show) SystemUi.apk   ------>