[ BZOJ 3445 ] Roadblock

\(\\\)

\(Description\)


给出一张\(N\) 个点\(M\)条边的无向图,选择一条边使其权值翻倍,求操作后比操作前最短路长度增量最大值。

  • \(1\le N\le 250\)\(1\le M\le 250000\)

\(\\\)

\(Solution\)


首先这么稠密的图SPFA肯定爆炸

  • 注意到枚举哪条边权值翻倍再跑一遍最短路的做法复杂度是\(\Theta(M^2logN)\),显然会超时。
  • 发现如果不使最短路上的边权翻倍最短路并不会发生变化,所以只需要考虑对原图最短路上的边操作即可。
  • 确定最短路的边可以通过反向枚举,若发现\(dis[v]=dis[u]-e[i].w\)则找到了上一个点。

\(\\\)

\(Code\)


#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define N 255
#define M 250010
#define R register
#define gc getchar
using namespace std;

bool vis[N];
int n,m,res,ans,tot=1,hd[N],dis[N];

struct edge{int to,nxt,w;}e[M<<1];
inline void add(int u,int v,int w){
  e[++tot].w=w; e[tot].to=v;
  e[tot].nxt=hd[u]; hd[u]=tot;
}

inline int rd(){
  int x=0; bool f=0; char c=gc();
  while(!isdigit(c)){if(c=='-')f=1;c=gc();}
  while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
  return f?-x:x;
}

priority_queue<pair<int,int> > q;
inline void dij(){
  memset(vis,0,sizeof(vis));
  memset(dis,0x3f,sizeof(dis));
  dis[1]=0; q.push(make_pair(0,1));
  while(!q.empty()){
    int u=q.top().second; q.pop();
    if(vis[u])continue; vis[u]=1;
    for(R int i=hd[u],v;i;i=e[i].nxt)
      if(dis[v=e[i].to]>dis[u]+e[i].w){
        dis[v]=dis[u]+e[i].w;
        q.push(make_pair(-dis[v],v));
      }
  }
}

vector<int> s;
inline void find(){
  int u=n;
  while(u!=1){
      for(R int i=hd[u],v;i;i=e[i].nxt)
      if(dis[v=e[i].to]==dis[u]-e[i].w){
        s.push_back(i); u=v; break;
      }
  }
}

int main(){
  n=rd(); m=rd();
  for(R int i=1,u,v,w;i<=m;++i){
    u=rd(); v=rd(); w=rd();
    add(u,v,w); add(v,u,w);
  }
  dij(); res=dis[n]; find();
  for(R int i=0;i<(int)s.size();++i){
    e[s[i]].w<<=1; e[s[i]^1].w<<=1;
    dij(); ans=max(ans,dis[n]-res);
    e[s[i]].w>>=1; e[s[i]^1].w>>=1;
  }
  printf("%d\n",ans);
  return 0;
}
posted @ 2018-09-04 19:22  SGCollin  阅读(183)  评论(0编辑  收藏  举报