CF1163F

BZOJ2725&CF1164F
CF1164F
题意

给定一个无向图,n个点,m条边及其权值,每次修改一条边的权值,询问相互独立,对于每次询问输出从1到n的最短路
分析

最短路树+线段树优化查询

1 先从1跑单源最短路dforward,在从n跑单源最短路dbackward

2 剥离出来任意一条从1到n的最短路,并对路径上的点进行编号
结论:
如果修改的边不再最短路上,那么最短路只有两种可能,1原来的最短路,2 经过u->v 的最短路,这两者取小即可
如果修改的边在最短路上
如果是减小,那么肯定还是这条路
如果是增大,那么可能还是这条路,或者不经过这条边的其它最短路
本题最重要的是求不经过某条边的最短路,怎么来求呢,我们需要知道什么是最短路树,我们从1跑单源最短路的时候就求得了最短路树,即n个点,n-1(假设都能到达)条边的树,1到每一个点的路径都是唯一的,这可以通过,每次更新的时候标记前驱节点来实现,如果一条边不在最短路上,那么他肯定与原来的最短路树形成了环,那么它就可能对这条环上的节点最短路产生贡献,我们需要对所有点进行分层,按照最短路的位置分层即每个点的层就是离他最近的最短路上的点的层数,可以通过一遍dfs来实现,一条不在最短路上的边必定连接了两个层(连接同一层是没有意义的,不会有贡献),我们建一棵线段树,将这两个层的值更新一下 dforward[u]+w+dbackward[v]

#include <bits/stdc++.h>
#define mem(ar,num) memset(ar,num,sizeof(ar))
#define me(ar) memset(ar,0,sizeof(ar))
#define lowbit(x) (x&(-x))
#define Pb push_back
#define  FI first
#define  SE second
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define IOS ios::sync_with_stdio(false)
#define DEBUG ;//cout<<endl<<"DEBUG"<<endl;
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
const int    prime = 999983;
const int    INF = 0x7FFFFFFF;
const LL     INFF =0x7FFFFFFFFFFFFFFF;
const double pi = acos(-1.0);
const double inf = 1e18;
const double eps = 1e-6;
const LL     mod = 1e9 + 7;
LL qpow(LL a,LL b){LL s=1;while(b>0){if(b&1)s=s*a%mod;a=a*a%mod;b>>=1;}return s;}
LL gcd(LL a,LL b) {return b?gcd(b,a%b):a;}
int dr[2][4] = {1,-1,0,0,0,0,-1,1};
typedef pair<LL,int> P;
const int maxn = 4e5+10;
#define ls (o<<1)
#define rs (o<<1|1)
#define int long long
LL tree[maxn<<2];
void modify(int x,int l,int r,int ql,int qr,long long v){
    if(l >= ql&&r <= qr){
        tree[x] = min(tree[x],v);
        return;
    }
    int mid = (l+r)>>1;
    if(ql <= mid)
        modify(x<<1,l,mid,ql,qr,v);
    if(qr > mid)
        modify(x<<1|1,mid+1,r,ql,qr,v);

}
long long query(int x,int l,int r,int p){
    if(l == r) return tree[x];
    int mid = (l+r)>>1;
    if(p <= mid)
        return min(tree[x],query(x<<1,l,mid,p));
    else
        return min(tree[x],query(x<<1|1,mid+1,r,p));
}
struct Edge{
    int from,to;LL w;
};
vector<Edge> edges;
vector<int> G[maxn];
void AddEdge(int u,int v,LL  w){
    edges.push_back(Edge{u,v,w});
    edges.push_back(Edge{v,u,w});
    int m = edges.size();
    G[u].Pb(m-2);
    G[v].Pb(m-1);
}
LL disf[maxn],disb[maxn],dis[maxn];
bool done[maxn];
int  p[maxn];
void Dij(int s){
    memset(dis,0x3f,sizeof(dis));
    memset(done,0,sizeof(done));
    memset(p,-1,sizeof(p));
    dis[s] = 0;
    priority_queue<P> Q;
    Q.push(P(dis[s],s));
    while(!Q.empty()){
        int u = Q.top().second;Q.pop();
        if(done[u]) continue;
        done[u] = true;
        for(auto id: G[u]){
            int to = edges[id].to,w = edges[id].w;
            if(done[to]) continue;
            if(dis[to] > dis[u]+w){
                dis[to] = dis[u]+w;
                p[to] = id;
                // cout<<to<<" "<<p[to]<<endl;
                Q.push(P(-dis[to],to));
            }
        }
    }
}

int path[maxn],id[maxn];
bool mainpath[maxn];
vector<int> E[maxn];// 抽出来的最短路树图
void dfs(int u){
    for(auto v:E[u]){
        if(!id[v])
            id[v] = id[u];
        dfs(v);
    }
}

int32_t main(void){
    IOS;
    // cout<<(0x3f)<<endl;
    memset(tree,0x3f,sizeof(tree));
    int n,m,q;
    cin>>n>>m>>q;
    for(int i = 0;i < m; ++i){
        LL u,v,w;
        cin>>u>>v>>w;
        AddEdge(u,v,w);
    }
    // 两次dij最短路

    Dij(n);memcpy(disb,dis,sizeof(dis));
    Dij(1);memcpy(disf,dis,sizeof(dis));
    // DEBUG;
    // for(int i= 1;i <= n; ++i)
    // {
    //     cout<<i<<" "<<disf[i]<<" "<<disb[i]<<endl;
    // }
    // DEBUG;
    // cout<<p[4]<<endl;
    int len = 0;
    // int i
    for(int i = n;i >= 1; ){
        path[++len] = i;
        if(i == 1) break;
        // cout<<p[i]<<endl;
        assert(p[i] != -1);
        mainpath[p[i]>>1] = true;
        i = edges[p[i]].from;
    }
    // assert()
    reverse(path+1,path+len+1);
    // cout<<len<<endl;
    for(int i = 1;i <= len; ++i)
        id[path[i]] = i;
    for(int i = 2;i <= n; ++i){
        if(p[i]>=0){
            int u = edges[p[i]].from;
            assert(edges[p[i]].to = i);
            E[u].Pb(i);
        }
    }
    dfs(1);
    // if(n == 420 )
    // {
    //     cout<<len<<endl;
    //     // assert(1==0);
    // }
    // for(int i = 1;i <= n; ++i)
    //     cout<<i<<" "<<id[i]<<endl;
    for(int i = 0;i < edges.size(); i ++){
        if(mainpath[i>>1]) continue;
        int u = edges[i].from;
        int v = edges[i].to;
        LL w = edges[i].w;
        if(id[u] >= id[v])
            continue;
        // DEBUG;
        // cout<<u<<" "<<v<<" "<<disf[u]+disb[v]+w<<endl;
        modify(1,1,len,id[u],id[v]-1,disf[u]+disb[v]+w);
    }
    

    // cout<<"QUery"<<endl;
    int e;LL noww;
    for(int i = 1;i <= q; ++i){
        cin>>e>>noww;e--;
        LL ans = INFF;
        // assert(2*e < edges.size());
        int u = edges[2*e].from,v = edges[2*e].to,w = edges[2*e].w;
        if(id[u] > id[v])
            swap(u,v);
        // cout<<w<<endl;
        // if(i == 84)
        //     cout<<mainpath[e]<<endl;
        if(mainpath[e]){
            // cout<<dis[n]-w+noww<<" "<<query(1,1,len,id[u])<<endl;
            ans = min(dis[n]-w+noww,query(1,1,len,id[u]));
        }
        else{
            // cout<<u<<" "<<disf[u]<<" "<<" "<<v<<" "<<disb[v]<<endl;
            ans = min(dis[n],min(disf[u]+disb[v],disf[v]+disb[u])+noww);
        }
        // printf("%lld\n",ans);
        cout<<ans<<endl;
    }
    







    return 0;
}


BZOJ2725

给定一个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]。

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值