bzoj2725 故乡的梦

44 篇文章 0 订阅
15 篇文章 0 订阅

bzoj2725 故乡的梦

题目描述
https://www.lydsy.com/JudgeOnline/problem.php?id=2725

题解
给你一张图,每次删除一条边问当前的最短路。
先随便拉一条最短路出来,如果删除的边不在这条最短路上那么答案就是原图最短路。
如果删除的边在最短路上,那么新的最短路肯定是从s开始先在最短路上跑一段,在外面跑一段再回到最短路上。
对于每条不在最短路上的边,我们找到它对应的最短路链上对于S和对于T的最近点,len=ws[i]+wt[i]+edge,两个最近点分别为L,R。
如果删除的边在[L,R]内,那么这组最短路就可行。所以我们建一棵线段树,对于每条边求出[L,R]并把这个区间赋个最小值,查询的时候在线段树上对应查询就好了。

代码

#include<bits/stdc++.h>
#define ll long long
#define inf 9999999999999999ll
#define N 200010
using namespace std;
int n,m,Q,ss,tt,k=1,cnt,pre[N],la[N],ff[N*2];
int g[2][N],tag[N],po[N],d[N];ll w[2][N];
struct node{int a,b,c;}e[N*2];
struct info{
  int x;ll y;
  bool operator<(const info &p)const{return y>p.y;}
};
struct data{ll minn;}t[N*4];
priority_queue<info>q;
map<int,int>h[N];
void add(int a,int b,int c)
{
  e[++k]=(node){a,b,c};ff[k]=la[a];la[a]=k;
  e[++k]=(node){b,a,c};ff[k]=la[b];la[b]=k;
  h[a][b]=h[b][a]=(k>>1);
}

int find(int x,int p)
{
  if(!x)return 0;
  if(g[p][x])return g[p][x];
  return g[p][x]=find(e[pre[x]].a,p);
}
void dij(int S,int p)
{
  for(int i=1;i<=n;i++)w[p][i]=inf;
  q.push((info){S,w[p][S]=0});pre[S]=0;
  while(q.size())
  {
    info tmp=q.top();q.pop();
	int x=tmp.x;ll y=tmp.y;
	if(w[p][x]!=y)continue;
	for(int a=la[x];a;a=ff[a])
	  if(w[p][e[a].b]>y+e[a].c)
	  {
	    pre[e[a].b]=a;w[p][e[a].b]=y+e[a].c;
	    q.push((info){e[a].b,w[p][e[a].b]});
	  }
  }
  if(!p)
  {
  	int pos=tt;
    while(pos)
    {
      tag[pre[pos]>>1]=1;g[p][pos]=pos;cnt++;
	  d[po[cnt]=pos]=cnt;pos=e[pre[pos]].a;
	}
  }
  else for(int i=1;i<=cnt;i++)g[p][po[i]]=po[i];
  for(int i=1;i<=n;i++)g[p][i]=find(i,p);
}

class seg_tree
{
  public:
  void build(int x,int l,int r)
  {
  	t[x].minn=inf;if(l==r)return;
    int mid=l+r>>1,lc=x<<1,rc=lc+1;
    build(lc,l,mid);build(rc,mid+1,r);
  }
  void modify(int x,int l,int r,int ql,int qr,ll val)
  {
    if(ql<=l&&r<=qr){
	  t[x].minn=min(t[x].minn,val);return;
    }
    int mid=l+r>>1,lc=x<<1,rc=lc+1;
    if(ql<=mid)modify(lc,l,mid,ql,qr,val);
    if(qr>mid)modify(rc,mid+1,r,ql,qr,val);
  }
  ll qry(int x,int l,int r,int pos)
  {
    if(l==r)return t[x].minn;
    int mid=l+r>>1,lc=x<<1,rc=lc+1;
	ll res=t[x].minn;
    if(pos<=mid)res=min(res,qry(lc,l,mid,pos));
	else res=min(res,qry(rc,mid+1,r,pos));
    return res;
  }
}T;

int main()
{
  //freopen("1.in","r",stdin);
  int a,b,c,L,R;ll len,ans;
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++)
    scanf("%d%d%d",&a,&b,&c),add(a,b,c);
  scanf("%d%d",&ss,&tt);
  dij(ss,0);dij(tt,1);T.build(1,1,cnt);
  for(int i=2;i<=k;i++)
  {
    if(tag[i>>1])continue;
	L=d[g[0][e[i].a]];R=d[g[1][e[i].b]];
    len=w[0][e[i].a]+w[1][e[i].b]+e[i].c;
    if(L>R)swap(L,R);
    if(L&&R&&L<R)T.modify(1,1,cnt,L,R-1,len);
  }
  scanf("%d",&Q);
  while(Q--)
  {
    scanf("%d%d",&a,&b);
    if(!tag[h[a][b]])ans=w[0][tt];
    else ans=T.qry(1,1,cnt,min(d[a],d[b]));
	if(ans==inf)printf("Infinity\n");
	else printf("%lld\n",ans);
  }
  return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值