1576: [Usaco2009 Jan]安全路经Travel 最短路径树+树链剖分+线段树

神题Orz。

我们可以先dijkstra求出最短路径树。然后有一些不在树上的边,加入后一定会形成一个环,那么此时就为一些点提供了次短路径。
假设我们加入了一条边(u,v),令t=lca(u,v),对于在链t->v上的点x,我们可以走这样一条路线:1->t->u->v->x,画个图可以发现,此时的长度为dis[u]+len(u,v)+dis[v]-dis[x]。dix[x]是已知的,要使结果最小,我们就要使dis[u]+len(u,v)+dis[v]最小。
然后我们可以用树链剖分和线段树来维护一下这个最小值。

#include<iostream>
#include<cstdio>
#define inf 1000000007
#define N 100005
using namespace std;
int n,m,cnt,sz,dfn;
int head[N],dis[N],from[N],belong[N],deep[N],size[N],id[N],fa[N][20];
int u[N<<1],v[N<<1],w[N<<1];
bool vis[N],mark[N<<2];
int next[N<<2],list[N<<2],key[N<<2],tag[N<<2],l[N<<2],r[N<<2];
struct node {int v,id;} heap[N];
inline int read()
{
    int a=0,f=1; char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
    while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
    return a*f;
}
inline void insert(int x,int y,int z)
{
    next[++cnt]=head[x];
    head[x]=cnt;
    list[cnt]=y;
    key[cnt]=z;
}
inline void push(node x)
{
    heap[++sz]=x;
    int now=sz;
    while (now&&heap[now].v<heap[now>>1].v)
    {
        swap(heap[now],heap[now>>1]);
        now>>=1;
    }
}
inline void pop()
{
    heap[1]=heap[sz--];
    int now=1;
    while (now<=(sz>>1))
    {
        int next=now<<1;
        if (next<sz&&heap[next].v>heap[next+1].v) next++;
        if (heap[now].v<heap[next].v) return;
        swap(heap[now],heap[next]);
        now=next;
    }
}
inline void dijkstra()
{
    for (int i=1;i<=n;i++) dis[i]=inf;
    dis[1]=0;
    push((node){0,1});
    while (sz)
    {
        int x=heap[1].id; pop();
        if (vis[x]) continue; vis[x]=1;
        for (int i=head[x];i;i=next[i])
            if (dis[list[i]]>dis[x]+key[i])
            {
                dis[list[i]]=dis[x]+key[i];
                mark[from[list[i]]]=0; 
                from[list[i]]=i; 
                mark[i]=1;
                push((node){dis[list[i]],list[i]});
            }
    }
}
void dfs1(int x)
{
    for (int i=1;(1<<i)<=deep[x];i++) fa[x][i]=fa[fa[x][i-1]][i-1];
    size[x]=1;
    for (int i=head[x];i;i=next[i])
        if (mark[i])
        {
            fa[list[i]][0]=x;
            deep[list[i]]=deep[x]+1;
            dfs1(list[i]);
            size[x]+=size[list[i]];
        }
}
void dfs2(int x,int chain)
{
    id[x]=++dfn; belong[x]=chain;
    int k=0;
    for (int i=head[x];i;i=next[i])
        if (mark[i]&&size[list[i]]>size[k]) k=list[i];
    if (!k) return;
    dfs2(k,chain);
    for (int i=head[x];i;i=next[i])
        if (mark[i]&&list[i]!=k) dfs2(list[i],list[i]);
}
inline int lca(int x,int y)
{
    if (deep[x]<deep[y]) swap(x,y);
    int t=deep[x]-deep[y];
    for (int i=0;(1<<i)<=t;i++)
        if ((1<<i)&t) x=fa[x][i];
    for (int i=19;i>=0;i--)
        if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return x==y?x:fa[x][0];
}
void build(int k,int x,int y)
{
    l[k]=x; r[k]=y; tag[k]=inf;
    if (l[k]==r[k]) return;
    int mid=l[k]+r[k]>>1;
    build(k<<1,x,mid); build(k<<1|1,mid+1,y);
}
inline void pushdown(int k)
{
    if (l[k]==r[k]|tag[k]==inf) return;
    tag[k<<1]=min(tag[k<<1],tag[k]);
    tag[k<<1|1]=min(tag[k<<1|1],tag[k]);
    tag[k]=inf;
}
void change(int k,int x,int y,int v)
{
    pushdown(k);
    if (x==l[k]&&r[k]==y)
    {
        tag[k]=min(tag[k],v);
        return;
    }
    int mid=l[k]+r[k]>>1;
    if (y<=mid) change(k<<1,x,y,v);
    else if (x>mid) change(k<<1|1,x,y,v);
    else change(k<<1,x,mid,v),change(k<<1|1,mid+1,y,v);
}
inline void solve_change(int x,int f,int v)
{
    while (belong[x]!=belong[f])
    {
        change(1,id[belong[x]],id[x],v);
        x=fa[belong[x]][0];
    }
    if (x!=f) change(1,id[f]+1,id[x],v);
}
int query(int k,int x)
{
    pushdown(k);
    if (l[k]==r[k]) return tag[k];
    int mid=l[k]+r[k]>>1;
    if (x<=mid) return query(k<<1,x);
    else return query(k<<1|1,x);
}
int main()
{
    n=read(); m=read();
    for (int i=1;i<=m;i++)
        u[i]=read(),v[i]=read(),w[i]=read(),insert(u[i],v[i],w[i]),insert(v[i],u[i],w[i]);
    dijkstra();
    dfs1(1);
    dfs2(1,1);
    build(1,1,n);
    for (int i=1;i<=m;i++)
    {
        int t=lca(u[i],v[i]);
        if (!mark[2*i-1]) solve_change(v[i],t,dis[u[i]]+dis[v[i]]+w[i]);
        if (!mark[2*i]) solve_change(u[i],t,dis[u[i]]+dis[v[i]]+w[i]);
    }
    for (int i=2;i<=n;i++)
    {
        int t=query(1,id[i]);
        if (t==inf) puts("-1"); else printf("%d\n",t-dis[i]);
    }
    return 0;
}

你可能感兴趣的:(1576: [Usaco2009 Jan]安全路经Travel 最短路径树+树链剖分+线段树)