zoukankan      html  css  js  c++  java
  • 【BZOJ】1576 [Usaco2009 Jan]安全路经Travel

    【算法】最短路树+(树链剖分+线段树)||最短路树+并查集

    【题解】

    两种方法的思想是一样的,首先题目限制了最短路树唯一。

    那么建出最短路树后,就是询问对于每个点断掉父边后重新找路径的最小值,其它路径只能是这个点和其子树节点通过非树边到达非子树节点。

    这样考虑很难统计,换个角度考虑每条非树边的影响。

    一条非树边连接两个端点u,v,它们会有一个LCA,那么这条非树边就可以影响u~LCA和v~LCA两条链上的点。

    这样依然不方便统计,因为两条链上每个点的影响各不相同,所以使用差分的思想。

    定义一条非树边对两条链上的点的贡献为g[i]=dis[u]+dis[v]+e[i].w,那么对于两条链上的每个点就是ans[x]=min(ans[x],g[i]-dis[x]),因为dis[x]是每个点自身属性,那么就可以统一地对两条链上上的点赋值g[i]。

    现在,我们可以明确每条非树边对特定的两条边的贡献,那么显然可以用树链剖分+线段树对两条链上的点进行【区间最小值覆盖+单点查询最小值】,这一操作可以用标记永久化实现。

    考虑另一种写法,如果我们把非树边按贡献排序,那么贡献小的覆盖之后,贡献大的就不可能影响到这些被覆盖过的点了,那么可以将覆盖过的点用并查集合并为一个点,遇到直接跳。

    复杂度O(m log n)。

    <并查集>

    #include<cstdio>
    #include<algorithm>
    #include<queue>
    #include<cctype>
    #include<cstring>
    using namespace std;
    const int maxn=400010,inf=0x3f3f3f3f;
    struct edge{int u,v,w,from;}e[maxn*2];
    int n,m,cnt,ans[maxn],first[maxn],tot,fa[maxn],f[maxn],deep[maxn],dis[maxn],d[maxn],c[maxn];
    int read()
    {
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    void insert(int u,int v,int w)
    {tot++;e[tot].u=u;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    struct cyc{
        int x,d;
        bool operator < (const cyc &a)const{
            return d>a.d;
        }
    };
    priority_queue<cyc>q;
    void dijkstra(){
        memset(d,0x3f,sizeof(d));
        deep[1]=d[1]=0;q.push((cyc){1,0});
        while(!q.empty()){
            cyc x=q.top();q.pop();
            if(x.d!=d[x.x])continue;
            for(int i=first[x.x];i;i=e[i].from)if(d[e[i].v]>d[x.x]+e[i].w){
                d[e[i].v]=d[x.x]+e[i].w;
                deep[e[i].v]=deep[x.x]+1;
                f[e[i].v]=x.x;
                q.push((cyc){e[i].v,d[e[i].v]});
            }
        }
    }
    struct cyc2{int u,v,num;}b[maxn];
    bool cmp(cyc2 a,cyc2 b){return a.num<b.num;}
    int main(){
        n=read();m=read();
        int u,v,w;
        for(int i=1;i<=m;i++){
            u=read();v=read();w=read();
            insert(u,v,w);insert(v,u,w);
        }
        dijkstra();
        for(int i=1;i<=tot;i+=2){
            if(deep[e[i].u]<deep[e[i].v])swap(e[i].u,e[i].v);
            if(d[e[i].u]!=d[e[i].v]+e[i].w)b[++cnt]=(cyc2){e[i].u,e[i].v,d[e[i].u]+d[e[i].v]+e[i].w};
        }
        sort(b+1,b+cnt+1,cmp);
        for(int i=1;i<=n;i++)fa[i]=i;
        f[1]=1;//初始父亲 
        for(int i=1;i<=cnt;i++){
            int x=find(b[i].u),y=find(b[i].v);
            while(x!=y){
                if(deep[x]<deep[y])swap(x,y);
                if(!ans[x])ans[x]=b[i].num;
                x=fa[x]=find(f[x]);
            }
        }
        for(int i=2;i<=n;i++)if(!ans[i])printf("-1
    ");else printf("%d
    ",ans[i]-d[i]);
        return 0;
    }
    View Code

    补充说明:【排序+并查集】是一种套路,处理MST的著名算法kruskal就是使用这种思想。这种做法要求无后效性,将价值最大的边纳入然后并成一个点继续处理,从而保证最优性。

    <树链剖分+线段树>

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int inf=0x3f3f3f3f,maxn=100010,maxm=200010;
    struct edge{int u,from,v,w;}e[maxm*3];
    struct tree{int l,r,tag;}t[maxn*3];
    int n,m,tot=0,first[maxn],q[100010],fa[maxn],deep[maxn],d[maxn],top[maxn],pos[maxn],size[maxn],te[maxn],dfsnum=0;
    bool mark[maxm*3],vis[maxn];
    void insert(int u,int v,int w)
    {tot++;e[tot].u=u;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
    void spfa()
    {
        memset(d,0x3f,sizeof(d));
        memset(mark,0,sizeof(mark));
        memset(vis,0,sizeof(vis));
        int head=0,tail=1;q[0]=1;vis[1]=1;d[1]=0;
        while(head!=tail)
         {
             int x=q[head++];if(head>100000)head=0;
             for(int i=first[x];i;i=e[i].from)
              if(d[e[i].v]>d[x]+e[i].w)
               {
                   int y=e[i].v;
                   d[y]=d[x]+e[i].w;
                   fa[y]=x;
                   mark[te[y]]=0;
                   te[y]=i;
                   mark[i]=1;
                   if(!vis[y]){q[tail++]=y;if(tail>100000)tail=0;}
                   vis[y]=1;
               }
             vis[x]=0;
         }
    //    for(int i=1;i<=n;i++)printf("fa[%d]=%d d[%d]=%d
    ",i,fa[i],i,d[i]);
    //    for(int i=1;i<=tot;i++)printf("[%d]%d %d %d
    ",i,e[i].u,e[i].v,mark[i]);
    }
    void build(int k,int l,int r)
    {
        t[k].l=l;t[k].r=r;t[k].tag=inf;
        if(l==r)return;
        int mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
    }
    void dfs1(int x)
    {
        size[x]=1;
        for(int i=first[x];i;i=e[i].from)
         if(mark[i])
          {
              int y=e[i].v;
              deep[y]=deep[x]+1;
              dfs1(y);
              size[x]+=size[y];
          }
    }
    void dfs2(int x,int tp)
    {
        int k=0;
        top[x]=tp;
        pos[x]=++dfsnum;
        for(int i=first[x];i;i=e[i].from)
         if(mark[i]&&size[e[i].v]>size[k])k=e[i].v;
        if(k==0)return;
        dfs2(k,tp);
        for(int i=first[x];i;i=e[i].from)
         if(mark[i]&&e[i].v!=k)dfs2(e[i].v,e[i].v);
    }
    void seg_insert(int k,int l,int r,int x)
    {
        if(l<=t[k].l&&t[k].r<=r)
         {
             t[k].tag=min(t[k].tag,x);
             return;
         }
        else
         {
             int mid=(t[k].l+t[k].r)>>1;
             if(l<=mid)seg_insert(k<<1,l,r,x);
             if(r>mid)seg_insert(k<<1|1,l,r,x);
         }
    }
    int seg_ask(int k,int x)
    {
        if(t[k].l==t[k].r)return t[k].tag;
        int mid=(t[k].l+t[k].r)>>1;
        if(x<=mid)return min(t[k].tag,seg_ask(k<<1,x));
         else return min(t[k].tag,seg_ask(k<<1|1,x));
    }
    void solve_ins(int x,int y,int w)
    {
        while(top[x]!=top[y])
         {
             if(deep[top[x]]<deep[top[y]])swap(x,y);
             seg_insert(1,pos[top[x]],pos[x],w);
             x=fa[top[x]];
         }
        if(pos[x]>pos[y])swap(x,y);
        if(pos[x]<pos[y])seg_insert(1,pos[x]+1,pos[y],w);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        int u,v,w;
        for(int i=1;i<=m;i++)
         {
             scanf("%d%d%d",&u,&v,&w);
             insert(u,v,w);insert(v,u,w);
         }
        spfa();
        build(1,1,n);dfs1(1);dfs2(1,1);//printf("sldf
    ");
        for(int i=1;i<=m;i++)
         if(!mark[i*2-1]&&!mark[i*2])solve_ins(e[i*2].u,e[i*2].v,d[e[i*2].u]+d[e[i*2].v]+e[i*2].w);
    //    printf("asfjld
    ");
        for(int i=2;i<=n;i++)
         {
             int ans=seg_ask(1,pos[i]);
             if(ans>inf-100)ans=d[i]-1;
             printf("%d
    ",ans-d[i]);
         }
        return 0;
    }
    SPFA+链剖+线段树
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>
    using namespace std;
    const int inf=0x3f3f3f3f,maxn=100010,maxm=200010;
    struct edge{int u,from,v,w;}e[maxm*3];
    struct tree{int l,r,tag;}t[maxn*3];
    int n,m,tot=0,first[maxn],fa[maxn],deep[maxn],d[maxn],top[maxn],pos[maxn],size[maxn],te[maxn],dfsnum=0;
    bool mark[maxm*3],vis[maxn];
    void insert(int u,int v,int w)
    {tot++;e[tot].u=u;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;}
    struct Node{int x,d;}cyc;
    priority_queue<Node>q;
    bool operator <(Node a,Node b)
    {return a.d>b.d;}
    void dijkstra()
    {
        memset(d,0x3f,sizeof(d));
        memset(mark,0,sizeof(mark));
        d[1]=0;cyc.d=0;cyc.x=1;q.push(cyc);
        while(!q.empty())
         {
             cyc=q.top();q.pop();
             int x=cyc.x;
             if(cyc.d!=d[x])continue;
             for(int i=first[x];i;i=e[i].from)
              if(d[e[i].v]>d[x]+e[i].w)
               {
                   int y=e[i].v;
                   d[y]=d[x]+e[i].w;
                   cyc.x=y;cyc.d=d[y];q.push(cyc);
                   mark[te[y]]=0;
                   te[y]=i;mark[i]=1;
                   fa[y]=x;
               }
         }
    //    for(int i=1;i<=n;i++)printf("fa[%d]=%d d[%d]=%d
    ",i,fa[i],i,d[i]);
    //    for(int i=1;i<=tot;i++)printf("[%d]%d %d %d
    ",i,e[i].u,e[i].v,mark[i]);
    }
    void build(int k,int l,int r)
    {
        t[k].l=l;t[k].r=r;t[k].tag=inf;
        if(l==r)return;
        int mid=(l+r)>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
    }
    void dfs1(int x)
    {
        size[x]=1;
        for(int i=first[x];i;i=e[i].from)
         if(mark[i])
          {
              int y=e[i].v;
              deep[y]=deep[x]+1;
              dfs1(y);
              size[x]+=size[y];
          }
    }
    void dfs2(int x,int tp)
    {
        int k=0;
        top[x]=tp;
        pos[x]=++dfsnum;
        for(int i=first[x];i;i=e[i].from)
         if(mark[i]&&size[e[i].v]>size[k])k=e[i].v;
        if(k==0)return;
        dfs2(k,tp);
        for(int i=first[x];i;i=e[i].from)
         if(mark[i]&&e[i].v!=k)dfs2(e[i].v,e[i].v);
    }
    void seg_insert(int k,int l,int r,int x)
    {
        if(l<=t[k].l&&t[k].r<=r)
         {
             t[k].tag=min(t[k].tag,x);
             return;
         }
        else
         {
             int mid=(t[k].l+t[k].r)>>1;
             if(l<=mid)seg_insert(k<<1,l,r,x);
             if(r>mid)seg_insert(k<<1|1,l,r,x);
         }
    }
    int seg_ask(int k,int x)
    {
        if(t[k].l==t[k].r)return t[k].tag;
        int mid=(t[k].l+t[k].r)>>1;
        if(x<=mid)return min(t[k].tag,seg_ask(k<<1,x));
         else return min(t[k].tag,seg_ask(k<<1|1,x));
    }
    void solve_ins(int x,int y,int w)
    {
        while(top[x]!=top[y])
         {
             if(deep[top[x]]<deep[top[y]])swap(x,y);
             seg_insert(1,pos[top[x]],pos[x],w);
             x=fa[top[x]];
         }
        if(pos[x]>pos[y])swap(x,y);
        if(pos[x]<pos[y])seg_insert(1,pos[x]+1,pos[y],w);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        int u,v,w;
        for(int i=1;i<=m;i++)
         {
             scanf("%d%d%d",&u,&v,&w);
             insert(u,v,w);insert(v,u,w);
         }
        dijkstra();
        build(1,1,n);dfs1(1);dfs2(1,1);//printf("sldf
    ");
        for(int i=1;i<=m;i++)
         if(!mark[i*2-1]&&!mark[i*2])solve_ins(e[i*2].u,e[i*2].v,d[e[i*2].u]+d[e[i*2].v]+e[i*2].w);
    //    printf("asfjld
    ");
        for(int i=2;i<=n;i++)
         {
             int ans=seg_ask(1,pos[i]);
             if(ans>inf-100)ans=d[i]-1;
             printf("%d
    ",ans-d[i]);
         }
        return 0;
    }
    Dijkstra+链剖+线段树

    事实证明,Dijkstra比SPFA稳得多,虽然也可能是故意卡的,但终归卡不了Dijkstra,因为本来理论上界就小。

    Dijkstra+Heap 2.5s

    SPFA+SLF 10s

    SPFA TLE

  • 相关阅读:
    理解钩子Hook以及在Thinkphp下利用钩子使用行为扩展
    ThinkPHP 分页类的使用及退出功能的实现
    ThinkPHP登录功能代码
    thinkphp遗留问题
    ThinkPHP随笔
    ThinkPhp循环出数据库中的内容并输出到模板
    thinkphp常用Config.php配置项
    thinkphp笔记
    PHP面向对象学习七 总结
    Trie树
  • 原文地址:https://www.cnblogs.com/onioncyc/p/6753170.html
Copyright © 2011-2022 走看看