[删边最短路 并查集] BZOJ 1576 [Usaco2009 Jan]安全路经Travel

24 篇文章 0 订阅
20 篇文章 0 订阅

hzwer是这么说的

首先用dijkstra得出最短路径树

然后

我的做法是树链剖分+线段树

对于一条不在最短路树的有向边(无向可看成两条有向)u-v,长度L,设t=lca(u,v)

那么对于t-v的路径上所有点x,都可通过1-t-u-v-x

路径长度为d[u]+L+d[v]-d[x]

最小化这个长度,也就是最小化d[u]+d[v]+L

所以我们可以用这个值去更新t-v所有点(不包括t)的最短路长度

这一步可以用线段树操作。。。


我把线段树改成了并查集


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
using namespace std;
typedef pair<int,int> abcd;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}

inline void read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

struct Queue{
  priority_queue<abcd,vector<abcd>,greater<abcd> > Q,del;
  void maintain(){ while (!Q.empty()&&!del.empty()&&Q.top()==del.top()) Q.pop(),del.pop(); }
  void push(abcd x){ maintain(); Q.push(x); }
  void pop(){ maintain(); if (!Q.empty()) Q.pop(); }
  void erase(abcd x){ maintain(); del.push(x); }
  abcd top(){ maintain(); return Q.top(); }
  bool empty(){ maintain(); return Q.empty(); }
}Q;

const int M=200005;
const int N=100005;
const int K=21;

struct edge{
  int u,v,w,next;
}G[M<<1];
int head[N],inum=1;
int tag[M<<1];

inline void add(int u,int v,int w,int p){
  G[p].u=u; G[p].v=v; G[p].w=w; G[p].next=head[u]; head[u]=p;
}

int n,m;
int dis[N];
#define U G[p].u
#define V G[p].v
#define W G[p].w
inline void Dij(int S){
  for (int i=1;i<=n;i++) dis[i]=1<<30;
  dis[S]=0; Q.push(abcd(0,S));
  for (int i=1;i<=n;i++){
    abcd t=Q.top(); Q.pop();
    int u=t.second;
    for (int p=head[u];p;p=G[p].next)
      if (dis[V]>dis[u]+G[p].w){
	Q.erase(abcd(dis[V],V));
	dis[V]=dis[u]+G[p].w;
	Q.push(abcd(dis[V],V));
      }
  }
}

int fat[N];
inline void init(int n){
  for (int i=1;i<=n;i++) fat[i]=i;
}
inline int Fat(int u){
  return u==fat[u]?u:fat[u]=Fat(fat[u]);
}

int father[N][K],depth[N];

inline void dfs(int u,int fa){
  father[u][0]=fa; depth[u]=depth[fa]+1;
  for (int k=1;k<K;k++) father[u][k]=father[father[u][k-1]][k-1];
  for (int p=head[u];p;p=G[p].next)
    if (tag[p] && V!=fa)
      dfs(V,u);
}

inline int LCA(int u,int v){
  if (depth[u]<depth[v]) swap(u,v);
  for (int k=K-1;~k;k--)
    if ((depth[u]-depth[v])>>k&1)
      u=father[u][k];
  if (u==v) return u;
  for (int k=K-1;~k;k--)
    if (father[u][k]!=father[v][k])
      u=father[u][k],v=father[v][k];
  return father[u][0];
}

struct data{
  int u,v,w;
  data(int u=0,int v=0,int w=0):u(u),v(v),w(w) { }
  bool operator < (const data &B) const {
    return w<B.w;
  }  
}edges[M<<1];
int tot;

int Ans[N];

inline void Solve(int u,int v,int w){
  int t=Fat(v),lca=LCA(u,v);
  while (depth[t]>depth[lca]){
    Ans[t]=w-dis[t];
    fat[t]=father[t][0];
    t=Fat(t);
  }
}

int main(){
  int iu,iv,iw;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  read(n); read(m);
  for (int i=1;i<=m;i++) 
    read(iu),read(iv),read(iw),add(iu,iv,iw,++inum),add(iv,iu,iw,++inum);
  Dij(1);
  for (int p=2;p<=inum;p++)
    if (dis[V]==dis[U]+W || dis[U]==dis[V]+W)
      tag[p]=1;
    else
      edges[++tot]=data(U,V,dis[U]+dis[V]+W);
  dfs(1,0);
  sort(edges+1,edges+tot+1);
  init(n);
  for (int i=1;i<=tot;i++)
    Solve(edges[i].u,edges[i].v,edges[i].w);
  for (int i=2;i<=n;i++)
    if (Ans[i])
      printf("%d\n",Ans[i]);
    else
      printf("-1\n");
  return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值