BZOJ2725 故乡的梦 [神题][Dinic][Dijsktra][BFS][Heap][Tarjan]

8 篇文章 0 订阅
2 篇文章 0 订阅

2725: [Violet 6]故乡的梦

Time Limit: 20 Sec   Memory Limit: 128 MB
Submit: 715   Solved: 222
[ Submit][ Status][ Discuss]

Description

Input

Output

Sample Input

6 7

1 2 1

2 3 1

3 4 2

4 5 1

5 6 1

1 3 3

4 6 3

1 6

4

1 2

1 3

4 3

6 5

Sample Output


7

6

Infinity

7

HINT


Source


别的不说光是这代码量我就服了,追求严谨,我写了网络流与Tarjan,各种花式作死。dinic只搞两次加了,当前弧没什么效果。。。还是用了优先队列,毕竟空间使用都是线性的,总的时间复杂度也很优,只有 O((n+m)+nlogn) ,算法的时间瓶颈上线是dijsktra带来的,换成SPFA是否有更好的表现?
直接无耻地引用题解 by DaD3zZ大佬%%%%

首先我们考虑,如果ST不连通,那显然全部输出Infinity

我们先做两遍 Dijsktra ,求出S出发到所有点的单源最短路 ds[] ,以及T出发到所有点的单源最短路 dt[]

那么我们发现,如果删除的边不存在最短路径上,那么显然答案全都是 ds[T]

考虑利用 ds[] dt[] 的信息,构建一种新的图 最短路径图Gs 即满足 ds[u]+val(u>v)=ds[v] 的边所构成的图,同理做出Gt

那么我们需要找到 Gs Gt 中的割边,因为只有割这种边,才会使得最短路径发生变化

那么问题在于如何求, Tarjan 的方法,正确性位置,不妨考虑最小割,
所以我们假定每条边容量为1,我们在 Gs 上进行增广,如果 MinCut>=2 ,那么任意删一条边对结果不造成影响,因为只需要知道是否>=2所以增广两次即可
那么当最小割为1时,我们需要求割边,这时候利用 Tarjan
在残余网络上跑tarjan求出所有SCC,记 belong[u] 为点u所在SCC的编号。显然有 belong[s]!=belong[t] (否则s到t有通路,能继续增广)。
①对于任意一条满流边 (u,v) (u,v) 能够出现在某个最小割集中,当且仅当 belong[u]!=belong[v]
②对于任意一条满流边 (u,v) (u,v) 必定出现在最小割集中,当且仅当 belong[u]==belong[s] belong[v]==belong[t] 。 应用:BZOJ1797

求出这些割边后,我们知道,删除非割边并不影响答案,所以输出 ds[T] ,只有割边会对答案有影响

同样的,Gs中的割边反向就是Gt中的割边
那么我们求出这些割边, cut(1 K) ,显然我们可以离线的处理出每条割边的答案,然后O(1)询问
我们对最短路径图 Gs 再进行改动,其中的割边我们设 val=1 ,非割边 val=0 ,然后我们可以求出 Gs Gt S/T 到每个点的最短路 0/1 构成
这个实现起来可以利用 BFS+双端队列
然后我们可以离线的处理出每个 cut 的答案,这个过程可以利用 multiset/heap/线 来实现
堆的方法:
维护一个小根堆,关键字是 ds[u]+val(u>v)+dt[v] ,具体的方法就是 当前计算的是now,先将之前的状态 Dt[v]>=Sumnow) 弹出,然后把当前的所有出边加入到堆中,然后用堆顶答案去更新接下来的值
线段树的方法:
区间覆盖取最小的经典模型,先修改,再DFS一遍整棵线段树即可
multiset的方法:
扫描cut的时候,记录所有可行的答案,然后取最小来更新即可

#include<bits/stdc++.h>
#define RG
using namespace std;
template<class T>inline void read(T &res){
    static char ch;T flag=1;
    while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;
    while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;res*=flag;
}
//------------------init---------------------
const int N = 200005;
const long long INF = (1LL<<50);
int S,T,n,m,k,head[N],cnt=1;
//----------------dijsktra-------------------
long long ds[N],dt[N];
struct Edge{
    int from,to,w,nxt;
}E[N<<1];
void addedge(int x,int y,int z){
    E[++cnt].nxt=head[x],head[x]=cnt,E[cnt].to=y,E[cnt].w=z,E[cnt].from=x;
    E[++cnt].nxt=head[y],head[y]=cnt,E[cnt].to=x,E[cnt].w=z,E[cnt].from=y;
}
#define pairs pair<long long ,int>
priority_queue<pairs,vector<pairs>,greater<pairs> > Q;
void dijsktra(int s,int t,long long dis[N]){
    fill(dis,dis+N,INF);
    Q.push(make_pair(0,s)),dis[s]=0;
    while(!Q.empty()){
        long long dist=Q.top().first;
        int u=Q.top().second;Q.pop();
        if(dist>dis[u])continue;
        for(RG int v,i=head[u];i;i=E[i].nxt){
            if(v=E[i].to,dis[u]+E[i].w<dis[v]){
                dis[v]=dis[u]+E[i].w;
                Q.push(make_pair(dis[v],v));
            }
        }
    }
}
//------------------dinic--------------------
int last[N],first[N],num=1,tot=1;
struct List{
    int nxt,to,from,w;
}L[N<<1];
struct Road{
    int nxt,to,from,cap,w;
}G[N<<1];
void addroad(int x,int y,int w){
    G[++tot].nxt=last[x],last[x]=tot,G[tot].to=y,G[tot].from=x,G[tot].cap=w;
    G[++tot].nxt=last[y],last[y]=tot,G[tot].to=x,G[tot].from=y,G[tot].cap=0;
}
void addlist(int x,int y,int w){
    L[++num].nxt=first[x],first[x]=num,L[num].to=y,L[num].from=x,L[num].w=w;
}
void createGraph(){
    for(RG int i=1;i<=n;i++){
        for(RG int j=head[i];j;j=E[j].nxt){
            if(ds[i]+E[j].w==ds[E[j].to])
                addroad(i,E[j].to,1);
            if(dt[E[j].to]==dt[i]+E[j].w)
                addlist(i,E[j].to,0);
        }
    }
}
int h[N],cur[N];
bool bfs(){
    queue<int> que;
    memset(h,-1,sizeof(h));
    que.push(S),h[S]=0;
    while(!que.empty()){
        int u=que.front();que.pop();
        for(RG int v,i=last[u];i;i=G[i].nxt){
            if(v=G[i].to,G[i].cap&&h[v]==-1)
             h[v]=h[u]+1,que.push(v);
        }
    }
    return h[T]!=-1;
}
int dfs(int u,int low){
    if(u==T||low==0)return low;
    int w,used=0;
    for(RG int &i=cur[u];i;i=G[i].nxt){
        if(G[i].cap&&h[G[i].to]==h[u]+1){
            w=dfs(G[i].to,min(low-used,G[i].cap));
            used+=w;
            G[i].cap-=w,G[i^1].cap+=w;
            if(used==low)return low;
        }
    }
    if(!used)h[u]=-1;
    return used;
}
int dinic(){
    int tmp=0,tim=2;
    while(bfs()&&tim--){
        for(register int i=1;i<=n;i++)cur[i]=last[i];
        tmp+=dfs(S,1);
    }
    return tmp;
}
//------------------Tarjan-------------------
int dfn[N],low[N],dft,st[N],sccon[N],scc,top,size[N];
bool insv[N];
void Tarjan(int u){
    low[u]=dfn[u]=++dft;
    insv[u]=1,st[++top]=u;
    for(RG int v,i=last[u];i;i=G[i].nxt){
        if(v=G[i].to,G[i].cap==0)continue;
        if(!dfn[v])Tarjan(v),low[u]=min(low[u],low[v]);
        else if(insv[v])low[u]=min(low[u],dfn[v]);
    }
    if(dfn[u]==low[u]){
        scc++;int v=0;
        while(u!=v)v=st[top--],size[scc]++,insv[v]=0,sccon[v]=scc;
    }
}
void Tarjan(){
    for(RG int i=1;i<=n;i++)
        if(!dfn[i])Tarjan(i);
}
//--------------double bfs-------------------
long long DS[N],DT[N];
bool markvis[N];
deque<int> dq;
void BFSF(){
    fill(DS,DS+N,INF);
    memset(markvis,0,sizeof(markvis));
    dq.push_back(S);DS[S]=0;
    while(!dq.empty()){
        int u=dq.front();dq.pop_front();
        if(markvis[u])continue;
        else markvis[u]=1;
        for(RG int v,i=last[u];i;i=G[i].nxt)if(v=G[i].to,!(i&1)){
            DS[v]=min(DS[u]+G[i].w,DS[v]);
            if(G[i].w)dq.push_back(v);
            else dq.push_front(v);
        }
    }
}
void BFSS(){
    fill(DT,DT+N,INF);
    memset(markvis,0,sizeof(markvis));
    dq.push_back(T);DT[T]=0;
    while(!dq.empty()){
        int u=dq.front();dq.pop_front();
        if(markvis[u])continue;
        else markvis[u]=1;
        for(RG int v,i=first[u];i;i=L[i].nxt){
            v=L[i].to,DT[v]=min(DT[u]+L[i].w,DT[v]);
            if(L[i].w)dq.push_back(v);
            else dq.push_front(v);
        }
    }
}
//------------------heap---------------------
struct node{
    int u,v;
    long long w;
    node(int u=0,int v=0,long long w=0):u(u),v(v),w(w){}
    inline bool operator  < (const node &rhs)const{
        return w>rhs.w;
    }
};
priority_queue<node> heap;
vector<int> VC[N];
int cut[N];
long long ans[N];
void work(){
    for(RG int i=1;i<=n;i++)
        if(ds[i]!=INF)VC[DS[i]].push_back(i);
    for(RG int i=0;i<scc;++i){
        int sz=VC[i].size();
        while(!heap.empty()&&DT[heap.top().v]>=DS[T]-i)heap.pop();
        for(RG int j=0;j<=sz-1;++j){
            int u=VC[i][j];
            for(RG int k=head[u];k;k=E[k].nxt)
                if(DS[E[k].to]>i&&cut[u]!=E[k].to)
                    heap.push(node(u,E[k].to,ds[u]+E[k].w+dt[E[k].to]));
        }
        if(!heap.empty())ans[i]=heap.top().w;
    }
}
//------------------main---------------------
int main(){
    //freopen("in.txt","r",stdin);
    read(n),read(m);
    for(RG int x,y,z,i=1;i<=m;i++)
        read(x),read(y),read(z),addedge(x,y,z);
    read(S),read(T);
    dijsktra(S,T,ds),dijsktra(T,S,dt);
    read(k);
    if(ds[T]==INF){
        while(k--)puts("Infinity");return 0;
    }
    createGraph();
    if(dinic()>=2){
        while(k--)printf("%lld\n",ds[T]);return 0;
    }
    Tarjan();
    for(RG int i=2;i<=tot;i+=2)
        if(!G[i].cap&&sccon[G[i].from]!=sccon[G[i].to])
            cut[G[i].from]=G[i].to,G[i].w=1;
    for(RG int i=2;i<=num;i++)
        if(cut[L[i].to]==L[i].from)L[i].w=1;
    BFSF();BFSS();work();
    for(RG int x,y,i=1;i<=k;i++){
        read(x),read(y);
        if(cut[x]==y){
            if(ans[DS[x]])printf("%lld\n",ans[DS[x]]);
            else puts("Infinity");
        }else if(cut[y]==x){
            if(ans[DS[y]])printf("%lld\n",ans[DS[y]]);
            else puts("Infinity");
        }else if(cut[x]!=y&&cut[y]!=x)printf("%lld\n",ds[T]);
    }
    return 0;
}

这里写图片描述

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值