zoukankan      html  css  js  c++  java
  • LCA离线算法Tarjan的模板

    hdu 2586:题意:输入n个点的n-1条边的树,m组询问任意点 a b之间的最短距离

    思路:LCA中的Tarjan算法,RMQ还不会。。

    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    using namespace std;
    const int N = 40010;
    const int M = 410;
    
    int head[N];            //树边邻接表的表头
    int __head[N];          //保存询问的邻接表的表头
    struct edge{            //保存边
        int u,v,w,next;
    }e[2*N];
    struct ask{            //保存询问
        int u,v,lca,next;
    }ea[M];
    int dir[N];              //保存点到树根的距离
    int fa[N];               //并查集,保存集合的代表元素
    int ance[N];             //保存集合的组合,注意对象是集合而不是元素
    bool vis[N];             //遍历时的标记数组
    
    inline void add_edge(int u,int v,int w,int &k) //保存边
    {
        e[k].u = u; e[k].v = v; e[k].w = w;
        e[k].next = head[u]; head[u] = k++;
        u = u^v; v = u^v; u = u^v;
        e[k].u = u; e[k].v = v; e[k].w = w;
        e[k].next = head[u]; head[u] = k++;
    }
    
    inline void add_ask(int u ,int v ,int &k) //保存询问
    {
        ea[k].u = u; ea[k].v = v; ea[k].lca = -1;
        ea[k].next = __head[u]; __head[u] = k++;
        u = u^v; v = u^v; u = u^v;   ///看上去深奥。。其实就是swap(u,v);
        ea[k].u = u; ea[k].v = v; ea[k].lca = -1;
        ea[k].next = __head[u]; __head[u] = k++;
    }
    
    int Find(int x)
    {
        return x == fa[x] ? x : fa[x] = Find(fa[x]);
    }
    void Union(int u ,int v)
    {
        fa[v] = fa[u];  //可写为  fa[Find(v)] = fa[u];
    }
    
    void Tarjan(int u)
    {
        vis[u] = true;
        ance[u] = fa[u] = u; //可写为 ance[Find(u)] = fa[u] = u;
        for(int k=head[u]; k!=-1; k=e[k].next)
            if( !vis[e[k].v] )
            {
                int v = e[k].v , w = e[k].w;
                dir[v] = dir[u] + w;
                Tarjan(v);
                Union(u,v);
                ance[Find(u)] = u;  
            }
        for(int k=__head[u]; k!=-1; k=ea[k].next)
            if( vis[ea[k].v] )
            {
                int v = ea[k].v;
                ea[k].lca = ea[k^1].lca = ance[Find(v)];
            }
    }
    
    int main()
    {
        int tcase;
        scanf("%d",&tcase);
        while(tcase--){
        int n,q;
        scanf("%d%d",&n,&q);
        memset(head,-1,sizeof(head));
        memset(__head,-1,sizeof(__head));
        int tot = 0;
        for(int i=1; i<n; i++)  //建树
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add_edge(u,v,w,tot);
        }
        tot = 0;
        for(int i=0; i<q; i++) //拆开保存询问
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add_ask(u,v,tot);
        }
        memset(vis,0,sizeof(vis));
        dir[1] = 0;
        Tarjan(1);
        for(int i=0; i<q; i++)
        {
            int s = i * 2 , u = ea[s].u , v = ea[s].v , lca = ea[s].lca;
            printf("%d
    ",dir[u]+dir[v]-2*dir[lca]);
        }
        }
    
        return 0;
    }

    hdu 2874:和上题一样都是求两点之间的最短距离,但是有多棵树,所以存在不连通的情况(用father判断一下就OK),,然后华丽丽的 超内存,拿别人的代码也是MLE

    #include <stdio.h>
    #include <iostream>
    #include <string.h>
    using namespace std;
    const int N = 10001;
    const int M = 1000001;
    
    int head[N];
    int __head[N];
    struct edge{
        int u,v,w,next;
    }e[2*N];
    struct ask{
        int u,v,lca,next;
    }ea[2*M];
    int dir[N];
    int fa[N];
    int ance[N];
    bool vis[N];
    
    inline void add_edge(int u,int v,int w,int &k) //保存边
    {
        e[k].u = u; e[k].v = v; e[k].w = w;
        e[k].next = head[u]; head[u] = k++;
        u = u^v; v = u^v; u = u^v;
        e[k].u = u; e[k].v = v; e[k].w = w;
        e[k].next = head[u]; head[u] = k++;
    }
    
    inline void add_ask(int u ,int v ,int &k) //保存询问
    {
        ea[k].u = u; ea[k].v = v; ea[k].lca = -1;
        ea[k].next = __head[u]; __head[u] = k++;
        u = u^v; v = u^v; u = u^v;   ///看上去深奥。。其实就是swap(u,v);
        ea[k].u = u; ea[k].v = v; ea[k].lca = -1;
        ea[k].next = __head[u]; __head[u] = k++;
    }
    
    int Find(int x)
    {
        return x == fa[x] ? x : fa[x] = Find(fa[x]);
    }
    void Union(int u ,int v)
    {
        int x = Find(u);
        int y = Find(v);
        fa[x] = y;
    }
    
    void Tarjan(int u)
    {
        vis[u] = true;
        ance[u] = fa[u] = u; //可写为 ance[Find(u)] = fa[u] = u;
        for(int k=head[u]; k!=-1; k=e[k].next)
            if( !vis[e[k].v] )
            {
                int v = e[k].v , w = e[k].w;
                dir[v] = dir[u] + w;
                Tarjan(v);
                Union(u,v);
                ance[Find(u)] = u;  //可写为ance[u] = u;  //甚至不要这个语句都行
            }
        for(int k=__head[u]; k!=-1; k=ea[k].next)
            if( vis[ea[k].v] )
            {
                int v = ea[k].v;
                ea[k].lca = ea[k^1].lca = ance[Find(v)];
            }
    }
    
    int main()
    {
        int k,n,q;
        while(scanf("%d%d%d",&n,&k,&q)!=EOF){
    
        memset(head,-1,sizeof(head));
        memset(__head,-1,sizeof(__head));
        int tot = 0;
        for(int i=0; i<k; i++)  //建树
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add_edge(u,v,w,tot);
        }
        tot = 0;
        for(int i=0; i<q; i++) //拆开保存询问
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add_ask(u,v,tot);
        }
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=n;i++){  ///访问每个节点
            if(!vis[i]){
                dir[i]=0;
                Tarjan(i);
            }
        }
        for(int i=0; i<q; i++)
        {
            int s = i * 2 , u = ea[s].u , v = ea[s].v , lca = ea[s].lca;  ///s代表偶数边,奇偶只是方向不同罢了,所以取一个就行
            if(fa[u]!=fa[v]) printf("Not connected
    ");  ///父亲结点不同当然就不是同一棵子树了
            else printf("%d
    ",dir[u]+dir[v]-2*dir[lca]);
        }
        }
    
        return 0;
    }
  • 相关阅读:
    BZOJ 1040 (ZJOI 2008) 骑士
    BZOJ 1037 (ZJOI 2008) 生日聚会
    ZJOI 2006 物流运输 bzoj1003
    ZJOI 2006 物流运输 bzoj1003
    NOI2001 炮兵阵地 洛谷2704
    NOI2001 炮兵阵地 洛谷2704
    JLOI 2013 卡牌游戏 bzoj3191
    JLOI 2013 卡牌游戏 bzoj3191
    Noip 2012 day2t1 同余方程
    bzoj 1191 [HNOI2006]超级英雄Hero——二分图匹配
  • 原文地址:https://www.cnblogs.com/liyinggang/p/5343953.html
Copyright © 2011-2022 走看看