BZOJ 2725: [Violet 6]故乡的梦

题意:给出一个无向图,给定起点和终点。给出q个询问,每个询问是删除某条边之后,起点到终点的最短路变成什么?询问之间是独立的。

 

这道题不错,但是蒟蒻完全不会做qaq,解法参考Claris大佬的。

首先容易想到如果删掉的边不在最短路上的话,最短路是不会变的。于是我们先跑一遍最短路,找出一条最短路。

终点是如果删掉最短路上的边怎么办?

这里我们反过来考虑,我们考虑,每一条非最短路边对最短路的贡献。首先分别以S和T为起点做一遍最短路记为d1[i]和d2[i],同时随便抓一条最短路Path并把它编号。

在以S为起点的最短路图,这必定是一个DAG,我们计算f1[i]代表(最早的)(在最短路Path上的)(能到达i点)的点是谁?

同理以T为起点的最短路图,我们计算f2[i]代表(最晚的)(在最短路Path上的)(能到达i点)的点是谁?

以上两个信息可以用拓扑排序DP计算得到。

那么对于每一条非最短路Path的边(x,y),它的贡献是什么:对于当区间 [ f1[x],f2[y] ]的边被删除时,边(x,y)能提供一条长度为d1[x]+len(x,y)+d2[y]的最短路,这里要注意所谓贡献,并不是只要删掉区间

[ f1[x],f2[y] ]的边,d1[x]+len(x,y)+d2[y] 就是答案,贡献的意思是这可以作为一种选择,并且这个选择是所有基于边(x,y)中最好的,因为d1[x]是正向最短路,d2[y]是反向最短路。

由上面这段路就得出做法了,我们用线段树维护这些所有选择,并选择最短的,这就是答案。

 

细节看代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> pii;
const LL N=4e5+10;
const LL INF=0x3f3f3f3f3f3f3f3f;
LL n,m,s,t,T,x[N],y[N],z[N],id[N],deg[N];
vector<LL> G[N];
LL tot,d1[N],d2[N],pre[N],path[N],f1[N],f2[N];

LL cnt=1,head[N],to[N<<1],nxt[N<<1],len[N<<1];
void add_edge(LL x,LL y,LL z) {
    nxt[++cnt]=head[x]; to[cnt]=y; len[cnt]=z; head[x]=cnt;
}

bool vis[N]; 
priority_queue<pii> q;
void Dijkstra(LL d[],LL s) {
    while (!q.empty()) q.pop();
    memset(vis,0,sizeof(vis));
    d[s]=0; q.push(make_pair(0,s));
    while (!q.empty()) {
        pii x=q.top(); q.pop();
        if (vis[x.second]) continue;
        vis[x.second]=1;
        for (LL i=head[x.second];i;i=nxt[i]) {
            LL y=to[i];
            if (d[y]>d[x.second]+len[i]) {
                d[y]=d[x.second]+len[i];
                q.push(make_pair(-d[y],y));
            }
        }
    }
}

queue<LL> Q;
void toposort(LL f[],LL now) {
    while (!Q.empty()) Q.pop();
    Q.push(now);
    while (!Q.empty()) {
        LL x=Q.front(); Q.pop();
        if (id[x]) f[x]=id[x];
        for (LL i=0;i<G[x].size();i++) {
            LL y=G[x][i];
            if (now==s) f[y]=min(f[y],f[x]);
            if (now==t) f[y]=max(f[y],f[x]);
            if (--deg[y]==0) Q.push(y);
        }
    }
}

LL Min[N<<2],tag[N<<2];
void build(LL rt,LL l,LL r) {
    Min[rt]=tag[rt]=INF;
    if (l==r) return;
    LL mid=l+r>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
}

void pushdown(LL rt) {
    Min[rt<<1]=min(Min[rt<<1],tag[rt]); Min[rt<<1|1]=min(Min[rt<<1|1],tag[rt]);
    tag[rt<<1]=min(tag[rt<<1],tag[rt]); tag[rt<<1|1]=min(tag[rt<<1|1],tag[rt]);
    tag[rt]=INF;
}

void update(LL rt,LL l,LL r,LL ql,LL qr,LL v) {
    if (ql<=l && r<=qr) { Min[rt]=min(Min[rt],v),tag[rt]=min(tag[rt],v); return; }
    LL mid=l+r>>1;
    pushdown(rt);
    if (ql<=mid) update(rt<<1,l,mid,ql,qr,v);
    if (qr>mid) update(rt<<1|1,mid+1,r,ql,qr,v);
    Min[rt]=min(Min[rt<<1],Min[rt<<1|1]);
}

LL query(LL rt,LL l,LL r,LL ql,LL qr) {
    if (ql<=l && r<=qr) return Min[rt];
    LL mid=l+r>>1,ret=INF;
    pushdown(rt);
    if (ql<=mid) ret=min(ret,query(rt<<1,l,mid,ql,qr));
    if (qr>mid) ret=min(ret,query(rt<<1|1,mid+1,r,ql,qr));
    return ret;
}

bool onpath(LL x,LL y) {
    if (!id[x] || !id[y]) return 0;
    if (id[x]+1==id[y] || id[y]+1==id[x]) return 1;
    return 0;
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out2.txt","w",stdout);
    cin>>n>>m;
    for (LL i=1;i<=m;i++) {
        scanf("%lld%lld%lld",&x[i],&y[i],&z[i]);
        add_edge(x[i],y[i],z[i]); add_edge(y[i],x[i],z[i]);
    }
    cin>>s>>t; cin>>T;
    if (s==t) { for (LL i=1;i<=T;i++) puts("0"); return 0; }
    memset(d1,0x3f,sizeof(d1));
    Dijkstra(d1,s);
    if (d1[t]>=INF) { for (LL i=1;i<=T;i++) puts("Infinity"); return 0; }
    memset(d2,0x3f,sizeof(d2));
    Dijkstra(d2,t);
    
    memset(deg,0,sizeof(deg));
    for (LL i=1;i<=m;i++) {
        if (d1[x[i]]+z[i]==d1[y[i]]) G[x[i]].push_back(y[i]),deg[y[i]]++,pre[y[i]]=x[i];
        if (d1[y[i]]+z[i]==d1[x[i]]) G[y[i]].push_back(x[i]),deg[x[i]]++,pre[x[i]]=y[i];
    } 
    for (LL i=t;i;i=pre[i]) path[++tot]=i,id[i]=tot;
    for (LL i=1;i<=n;i++) if (id[i]) id[i]=tot-id[i]+1;
    memset(f1,0x3f,sizeof(f1));
    toposort(f1,s);
    
    memset(deg,0,sizeof(deg));
    for (LL i=1;i<=n;i++) G[i].clear();
    for (LL i=1;i<=m;i++) {
        if (d2[x[i]]+z[i]==d2[y[i]]) G[x[i]].push_back(y[i]),deg[y[i]]++;
        if (d2[y[i]]+z[i]==d2[x[i]]) G[y[i]].push_back(x[i]),deg[x[i]]++;
    }
    memset(f2,0,sizeof(f2));
    toposort(f2,t);
    
    tot--; build(1,1,tot);
    for (LL i=1;i<=m;i++) 
        if (!onpath(x[i],y[i])) {
            LL t1=f1[x[i]],t2=f2[y[i]]; t2--;
            if (t1<INF&&t2>0&&t1<=t2) update(1,1,tot,t1,t2,d1[x[i]]+d2[y[i]]+z[i]);
            t1=f1[y[i]],t2=f2[x[i]]; t2--;
            if (t1<INF&&t2>0&&t1<=t2) update(1,1,tot,t1,t2,d1[y[i]]+d2[x[i]]+z[i]);
        }    
    
    while (T--) {
        LL x,y; scanf("%lld%lld",&x,&y);
        if (!onpath(x,y)) printf("%lld\n",d1[t]);
        else {
            x=id[x]; y=id[y]; if (x>y) swap(x,y); y--;
            LL ans=query(1,1,tot,x,y);
            if (ans>=INF) puts("Infinity"); else printf("%lld\n",ans);
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/clno1/p/10820268.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值