[bzoj 2725] 故乡的梦

1 篇文章 0 订阅
1 篇文章 0 订阅

丧病图论题……

这是个权限题……所以不放传送门了,说一下题目大意。
给定一个n个点,m条边的无向图,一个S,一个T,有Q个询问,每次询问删掉某条边之后S到T的最短路。(n,m,q<=200000)

先从S跑一遍最短路,再从T跑一遍最短路。然后我们考虑一个简化的问题:求删掉某条边之后S到T的最短路是否会变化。

可以看出,满足disS[u]+w(u,v)+disT[v]==disS[T]的边(u,v)一定在S到T的最短路上。我们把所有这样的边弄出来,跑一遍tarjan,那么当且仅当它是桥时S到T的最短路会变化。

这给我们提供了思路。于是,可以将这些tarjan跑出来的双连通分量缩起来。显然,这些双连通分量从S到T构成一条链,将它由S到T的顺序编号。

考虑删掉桥之后的答案,显然是用某一条非最短路上的边(u,v)代替,距离变成disS[u]+w(u,v)+disT[v]。

再考虑每条边可以代替哪些桥。
必须满足一下几个条件:
假设当前这座桥连接i到i+1的连通分量,当前边为(u,v)
1.可以用前i块连通分量的点走到u,且保证是S到u的最短路
2.可以用i之后连通分量的点走到v,且保证是T到v的最短路
于是,我们再跑两棵最短路树,一棵起点为S,另外一棵起点为T,然后给每个点一个belong1[i],一个belong2[i],即从哪个连通分量可以走S到i的最短路到i,从哪个连通分量可以走T到i的最短路到i。然而这样的连通分量可能有很多个,对于belong1,我们选编号最小的;对于belong2,我们选编号最大的。

我们从S到T的每个双连通分量扫描,每扫描到一块,加入这个连通分量到后面的边,删除前面的连通分量到它的边。用一个堆维护,关键字是disS[u]+w(u,v)+disT[v]。起点的连通分量为belong1[i],终点为belong2[i]。

啥都不说上代码。写得快哭了,用map还被卡常了……。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <deque>
#include <queue>
#include <map>
#include <set>
#include <ctime>
using namespace std;

typedef long long ll;
const int Maxn=200005;
const ll INF=ll(1)<<ll(60);

vector<int> bcc[Maxn];
int belong[Maxn],tt;

struct node
{
    int x;ll val;
    node(){} node(int _x,ll _val) { x=_x,val=_val; }
};

bool operator < (const node &a,const node &b) { return a.val>b.val; }
priority_queue<node> H;

struct Edge
{
    int from,to,cost;bool flag;
}E[Maxn<<1];
bool cmp(const Edge &a,const Edge &b) { return belong[a.from]<belong[b.from]; }

int Get()
{
    char ch; int v=0; bool f=false;
    while (!isdigit(ch=getchar())) if (ch=='-') f=true; v=ch-48;
    while (isdigit(ch=getchar())) v=v*10+ch-48;
    if (f) return -v;else return v;
}

void Put(ll x)
{
    char ch[21]; int tot=0;
    if (x==0) { puts("0");return; }
    while (x) ch[++tot]=x%10,x/=10;
    for (;tot;tot--) putchar(ch[tot]+48);puts("");
}

int n,m,q,tot,S,T,fst[Maxn],pre[Maxn<<1],to[Maxn<<1],from[Maxn<<1],cost[Maxn<<1],head,tail,Q[Maxn],
dfs_clock,dfn[Maxn],low[Maxn],col[Maxn],bridge[Maxn],belong2[Maxn];
ll disS[Maxn],disT[Maxn],ans[Maxn<<1];
bool vis[Maxn],flag[Maxn<<1],imp[Maxn<<1],imp2[Maxn<<1],is_bridge[Maxn<<1];

void add(int x,int y,int c)
{
    if (x==y) return;
    pre[++tot]=fst[x],fst[x]=tot,from[tot]=x,to[tot]=y,cost[tot]=c;
    E[tot].from=x,E[tot].to=y,E[tot].cost=c,E[tot].flag=false;
    pre[++tot]=fst[y],fst[y]=tot,from[tot]=y,to[tot]=x,cost[tot]=c;
    E[tot].from=y,E[tot].to=x,E[tot].cost=c,E[tot].flag=false;
}

void Dijkstra(int S,ll *dis)
{
    while (!H.empty()) H.pop();
    for (int i=1;i<=n;i++) vis[i]=false,dis[i]=INF;
    dis[S]=0; H.push(node(S,0)); vis[S]=true;
    while (!H.empty())
    {
        node cur=H.top(); H.pop();
        int x=cur.x;
        if (cur.val!=dis[x]) continue;
        for (int i=fst[x];i;i=pre[i])
        {
            int y=to[i];
            if (!vis[y] && dis[x]+ll(cost[i])<dis[y]) dis[y]=dis[x]+ll(cost[i]),H.push(node(y,dis[y])); 
        }
        vis[x]=true;
    }
}

void tarjan(int x,int prev)
{
    vis[x]=true; dfn[x]=low[x]=++dfs_clock;
    for (int i=fst[x];i;i=pre[i])
    {
        int y=to[i];
        if (i==(prev^1) || !flag[i]) continue;
        if (!vis[y]) tarjan(y,i),low[x]=min(low[x],low[y]);
        else low[x]=min(low[x],dfn[y]);
    }
    if (dfn[x]==low[x]) is_bridge[prev]=is_bridge[prev^1]=true;
}

void dfs(int x)
{   
    belong[x]=tot,vis[x]=true; bcc[tot].push_back(x);
    for (int i=fst[x];i;i=pre[i])
    {
        int y=to[i];
        if (!vis[y] && !is_bridge[i] && flag[i]) dfs(y);
    }
}

void getchain()
{   
    for (int i=1;i<=tot;i++) vis[i]=false;
    int cur=belong[S],cnt=0;
    while (cur!=belong[T])
    {   
        col[cur]=++cnt;vis[cur]=true;
        int len=bcc[cur].size(); bool ff=false;
        for (int i=0;i<len && !ff;i++)
        {
            int x=bcc[cur][i];
            for (int j=fst[x];j && !ff;j=pre[j])
                if (is_bridge[j] && !vis[belong[to[j]]]) { bridge[cnt]=j; cur=belong[to[j]]; ff=true; }
        }
    }
    col[cur]=++cnt;
    for (int i=1;i<=n;i++) belong[i]=col[belong[i]],belong2[i]=belong[i];
    for (int i=1;i<=tot;i++) while (!bcc[i].empty()) bcc[i].pop_back();
    tot=cnt;
    for (int i=1;i<=n;i++) bcc[belong[i]].push_back(i);
}

void bfs1(int cur)
{   
    head=tail=1; int len=bcc[cur].size();
    for (int i=0;i<len;i++) Q[tail++]=bcc[cur][i],vis[bcc[cur][i]]=true;
    while (head!=tail)
    {
        int x=Q[head++];
        for (int i=fst[x];i;i=pre[i])
        {
            int y=to[i];
            if (!vis[y] && imp[i]) Q[tail++]=y,belong[y]=cur,vis[y]=true;
        }
    }
}

void bfs2(int cur)
{   
    head=tail=1; int len=bcc[cur].size();
    for (int i=0;i<len;i++) Q[tail++]=bcc[cur][i],vis[bcc[cur][i]]=true;
    while (head!=tail)
    {
        int x=Q[head++];
        for (int i=fst[x];i;i=pre[i])
        {
            int y=to[i];
            if (!vis[y] && imp2[i]) Q[tail++]=y,belong2[y]=cur,vis[y]=true;
        }
    }
}

void init()
{
    n=Get(),m=Get(); int x,y,c; tot=1;
    for (int i=1;i<=m;i++) x=Get(),y=Get(),c=Get(),add(x,y,c);
    S=Get(),T=Get(); tt=tot;
    Dijkstra(S,disS); Dijkstra(T,disT);
    if (disS[T]==INF)
    {
        q=Get();
        while (q--) x=Get(),y=Get(),puts("Infinity");
        return;
    }

    for (int i=2;i<=tot;i+=2)
    {
        if (disS[from[i]]+ll(cost[i])+disT[to[i]]==disS[T] || disS[to[i]]+ll(cost[i])+disT[from[i]]==disS[T])
        flag[i]=flag[i^1]=true,E[i].flag=E[i^1].flag=true;
        else
        {
            if (disS[from[i]]+ll(cost[i])==disS[to[i]]) imp[i]=true;
            if (disT[from[i]]+ll(cost[i])==disT[to[i]]) imp2[i]=true;
            if (disS[to[i]]+ll(cost[i])==disS[from[i]]) imp[i^1]=true;
            if (disT[to[i]]+ll(cost[i])==disT[from[i]]) imp2[i^1]=true;
        }
    }
    for (int i=1;i<=n;i++) vis[i]=false;
    dfs_clock=0; tarjan(S,0);
    for (int i=1;i<=n;i++) vis[i]=false;
    tot=0; for (int i=1;i<=n;i++) if (!vis[i]) tot++,dfs(i);
    getchain();
    for (int i=1;i<=n;i++) vis[i]=false;
    for (int i=1;i<=tot;i++) bfs1(i);

    for (int i=1;i<=n;i++) vis[i]=false;
    for (int i=tot;i>=1;i--) bfs2(i);
}

void work()
{
    if (disS[T]==INF) return;
    while (!H.empty()) H.pop();
    sort(E+2,E+tt+1,cmp);
    for (int i=1,j=2;i<=tot-1;i++)
    {
        for (;j<=tt && belong[E[j].from]==i;j++)
            if (!E[j].flag) H.push(node(E[j].to,disS[E[j].from]+ll(E[j].cost)+disT[E[j].to]));
        while (!H.empty() && belong2[H.top().x]<=i) H.pop();
        if (!H.empty()) ans[bridge[i]]=ans[bridge[i]^1]=H.top().val;
        else ans[bridge[i]]=ans[bridge[i]^1]=INF;
    }
    for (int i=2;i<=tt;i++) if (!is_bridge[i]) ans[i]=disS[T];

    q=Get(); int x,y;
    while (q--)
    {
        x=Get(),y=Get(); ll res; int pos;
        for (int i=fst[x];i;i=pre[i]) if (to[i]==y) { pos=i;break; }
        if (x!=y) res=ans[pos];else res=disS[T];
        if (res!=INF) Put(res);else puts("Infinity");
    }
}

int main()
{   
   // freopen("hometown.in","r",stdin);
   // freopen("hometown.out","w",stdout);
    init();
    work();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值