zoukankan      html  css  js  c++  java
  • [LCA & RMQ] [NOIP2013] 货车运输

    首先看到这题, 由于要最大, 肯定是求最大生成树

    那么 o(n2) dfs 求任意点对之间的最小边是可以想到的

    但是看看数据范围肯定TLE

    于是暴力出来咯, 不过要注意query的时候判断的时候要 m+-1 但是递归下去要用m , 可以画图举特例分析

    1AC 代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<vector>
    #include<queue>
    #include<stack>
    #include<map>
    #include<set>
    #include<string>
    #include<iomanip>
    #include<ctime>
    #include<cctype>
    #include<algorithm>
    #ifdef WIN32
    #define AUTO "%I64d"
    #else
    #define AUTO "%lld"
    #endif
    using namespace std;
    #define smin(x,tmp) x=min((x),(tmp))
    const int INF=0x3f3f3f3f;
    const int maxn=10005;
    const int maxm=50005;
    
    int fa[maxn];
    int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); }
    inline bool union_find(int x,int y)
    {
        int t1=find(x),t2=find(y);
        if(t1==t2) return false;
        fa[t2]=t1;
        return true;
    }
    map <pair<int,int>,int> g;
    struct Edge
    {
        int to,next;
        int val;
    }edge[maxm<<1];
    int head[maxn];
    int maxedge;
    inline void addedge(int u,int v,int c)
    {
        edge[++maxedge]=(Edge){v,head[u],c};
        head[u]=maxedge;
        edge[++maxedge]=(Edge){u,head[v],c};
        head[v]=maxedge;
        g[make_pair(u,v)]=c;
        g[make_pair(v,u)]=c;
    }
    
    struct Road
    {
        int from,to;
        int cost;
        bool operator < (const Road t) const
        {
            return cost>t.cost;// querying the Biggest MST !!!!
        }
    }road[maxm];
    int n,m;
    
    int f[maxn],son[maxn],size[maxn],depth[maxn];
    int top[maxn],id[maxn],rid[maxn];
    int maxnode;//for segment tree (one-demensional array)
    int dfs1(int u,int father,int deep)
    {
        f[u]=father,size[u]=1,depth[u]=deep;
        for(int i=head[u];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v==father) continue;
            size[u]+=dfs1(v,u,deep+1);
            if(!son[u]||size[son[u]]<size[v]) son[u]=v;
        }
        return size[u];
    }
    void dfs2(int u,int tp)
    {
        top[u]=tp;id[u]=++maxnode;rid[maxnode]=u;// non de mixer la id-rid !!
        if(son[u]) dfs2(son[u],tp);
        for(int i=head[u];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v==f[u]||v==son[u]) continue;
            dfs2(v,v);
        }
    }
    int tree[maxn<<2];//min
    void build(int root,int l,int r)
    {
        if(r-l==1)
        {
            tree[root]=g[make_pair(rid[l],rid[r])];
            return;
        }
        int m=(l+r)>>1;
        build(root<<1,l,m);
        build(root<<1|1,m,r);
        tree[root]=min(tree[root<<1],tree[root<<1|1]);
    }
    int query(int root,int l,int r,int x,int y)//query min
    {
        if(x==y) return INF;//must!! when query the same node !!!!!!
        if(x<=l&&r<=y) return tree[root];
        int m=(l+r)>>1;
        int t1=INF,t2=INF;
        if(x<=m-1&&l<=y) t1=query(root<<1,l,m,x,y);//here too, use x<=m-1 in case stucking at m
        if(y>=m+1&&r>=x) t2=query(root<<1|1,m,r,x,y);//be conscious of m or m+1, query m but judge m+1
        return min(t1,t2);
    }
    int Find(int u,int v)//find min
    {
        int t1=top[u],t2=top[v];
        int ret=INF;
        while(t1^t2)
        {
            if(depth[t1]<depth[t2]) swap(t1,t2),swap(u,v);
            smin(ret,query(1,1,maxnode,id[t1],id[u]));
            smin(ret,g[make_pair(t1,f[t1])]);
            u=f[t1];
            t1=top[u];
        }
        if(depth[u]<depth[v]) swap(u,v);
        return min(ret,query(1,1,maxnode,id[v],id[u]));
    }
    
    
    inline void init()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++) scanf("%d%d%d",&road[i].from,&road[i].to,&road[i].cost);
        for(int i=1;i<=n;i++) fa[i]=i;
        memset(head,-1,sizeof(head));
        maxedge=-1;
    }
    void kruskal()
    {
        sort(road+1,road+m+1);
        int pos=1,tot=0;
        while(pos<=m && tot^(n-1))
        {
            if(union_find(road[pos].from,road[pos].to)) tot++,addedge(road[pos].from,road[pos].to,road[pos].cost);
            pos++;
        }
    }
    bool vis[maxn];//for union_find
    void build_forest()
    {
        for(int i=1;i<=n;i++)
        {
            int father=find(i);
            if(!vis[father])
            {
                vis[father]=true;
                dfs1(father,0,1);
                dfs2(father,father);
            }
        }
        build(1,1,maxnode);
    }
    int main()
    {
        freopen("truck.in","r",stdin);
        freopen("truck.out","w",stdout);
        init();
        kruskal();
        build_forest();
        int q;
        scanf("%d",&q);
        while(q--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(find(x)^find(y)) printf("-1
    ");
            else printf("%d
    ",Find(x,y));
        }
        return 0;
    }
    View Code

    但是NOIP正解一定不是链剖, 此题要用到 LCA 的 ST 倍增算法, 并且属于精确的查询,没有重叠部分, 可以用sum等进行替换, 边dfs边更新

    用 f[i][j] = f[f[i][j-1]][j-1] 保存 i 前面的第2i 节点, 与普通 RMQ 不同

    用 dp[i][j] = min ( dp[i][j-1] , dp[f[i][j-1]][j-1] ) 来维护倍增的最小值 ( 当然 sum 也一样 , 因为精确求范围, 满足区间加 )

    AC代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<vector>
    #include<queue>
    #include<stack>
    #include<map>
    #include<set>
    #include<string>
    #include<iomanip>
    #include<ctime>
    #include<cctype>
    #include<algorithm>
    #ifdef WIN32
    #define AUTO "%I64d"
    #else
    #define AUTO "%lld"
    #endif
    using namespace std;
    #define smin(x,tmp) x=min((x),(tmp))
    #define smax(x,tmp) x=max((x),(tmp))
    const int INF=0x3f3f3f3f;
    const int maxn=10005;
    const int maxm=50005;
    const int maxd=20;
    
    int fa[maxn];
    int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); }
    inline bool union_find(int x,int y)
    {
        int t1=find(x),t2=find(y);
        if(t1==t2) return false;
        fa[t2]=t1;
        return true;
    }
    
    struct Edge
    {
        int to,next;
        int val;
    }edge[maxm<<1];
    int head[maxn];
    int maxedge;
    inline void addedge(int u,int v,int c)
    {
        edge[++maxedge]=(Edge){v,head[u],c};
        head[u]=maxedge;
        edge[++maxedge]=(Edge){u,head[v],c};
        head[v]=maxedge;
    }
    
    struct Road
    {
        int from,to;
        int cost;
        bool operator < (const Road t) const
        {
            return cost>t.cost;
        }
    }road[maxm];
    int n,m;
    
    inline void init()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++) scanf("%d%d%d",&road[i].from,&road[i].to,&road[i].cost);
        for(int i=1;i<=n;i++) fa[i]=i;
        memset(head,-1,sizeof(head));
        maxedge=-1;
    }
    void kruskal()
    {
        sort(road+1,road+m+1);
        int pos=1,tot=0;
        while(pos<=m && tot^(n-1))
        {
            if(union_find(road[pos].from,road[pos].to)) tot++,addedge(road[pos].from,road[pos].to,road[pos].cost);
            pos++;
        }
    }
    
    int f[maxn][maxd+5],dp[maxn][maxd+5]; // the node 2^j after u 
    int depth[maxn];
    void dfs(int u,int deep)
    {
        depth[u]=deep;
        for(int k=1;(1<<k)<=n;k++)
        {
            f[u][k] = f[f[u][k-1]][k-1];
            dp[u][k] = min(dp[u][k-1] , dp[f[u][k-1]][k-1]);
        }
        for(int i=head[u];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(!depth[v])
            {
                f[v][0]=u;dp[v][0]=edge[i].val;//initialize here rather than in the former context!! no considering the roor coz INF is INF, not changing and not visiting the value!!
                dfs(v,deep+1);
            }
        }
    }
    
    int Find(int u,int v)
    {
        int ans=INF;
        if(depth[u] < depth[v]) swap(u,v);// making the u is deeper!!
        // make u v at the same depth
        for(int k=maxd;k>=0;k--) // k>=0 here!! or cannot jump at the same depth!!!
            if(depth[v] <= depth[f[u][k]]) // f[0] = 0, indicates its beyond the root!!
            {
                smin( ans , dp[u][k] );
                u=f[u][k];
            }
        if(u == v) return ans; //special judge of one of them is the LCA
        // jump at the same time 
        for(int k=maxd;k>=0;k--) // k>=0 here too!!
            if(f[u][k] ^ f[v][k])
            {
                smin(ans , min( dp[u][k] , dp[v][k] ) );
                u = f[u][k];
                v = f[v][k];
            }
        return min( ans , min(dp[u][0] , dp[v][0])); // u v this time is the F1 of LCA
    }
    
    int main()
    {
        freopen("truck.in","r",stdin);
        freopen("truck.out","w",stdout);
        init();
        kruskal();
        memset(dp,0x3f,sizeof(dp));
        for(int i=1;i<=n;i++)
            if(!depth[i]) dfs(i,1);
        int q;
        scanf("%d",&q);
        while(q--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            if(find(x)^find(y)) printf("-1
    ");
            else printf("%d
    ",Find(x,y));
        }
        return 0;
    }
    View Code

    然后的话粘一发 LCA 裸题 HDU 2874 

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #include<vector>
    #include<queue>
    #include<stack>
    #include<map>
    #include<set>
    #include<string>
    #include<iomanip>
    #include<ctime>
    #include<climits>
    #include<cctype>
    #include<algorithm>
    #ifdef WIN32
    #define AUTO "%I64d"
    #else
    #define AUTO "%lld"
    #endif
    using namespace std;
    const int maxn=10005;
    struct Edge
    {
        int to,next;
        int val;
    }edge[maxn*maxn];
    int head[maxn];
    int maxedge;
    inline void addedge(int u,int v,int c)
    {
        edge[++maxedge]=(Edge){v,head[u],c};
        head[u]=maxedge;
        edge[++maxedge]=(Edge){u,head[v],c};
        head[v]=maxedge;
    }
    int n,m,q;
    int fa[maxn];
    int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); }
    bool union_find(int x,int y)
    {
        int t1=find(x),t2=find(y);
        if(t1==t2) return false;
        fa[t2]=t1;
        return true;
    }
    int maxnode;
    int dfn[maxn],ver[maxn<<1];//dfn: first visit maxnode, ver: reverse function of dfn, indicating the number of vertex
    int depth[maxn<<1],dis[maxn];//depth: the depth of dfn, dis: from root to vertex
    inline bool init()
    {
        if(!~scanf("%d%d%d",&n,&m,&q)) return false;
        for(int i=1;i<=n;i++) fa[i]=i;
        memset(head,-1,sizeof(head));
        memset(dfn,0,sizeof(dfn));
        maxedge=-1;maxnode=0;
        for(int i=1;i<=m;i++)
        {
            int u,v,c;
            scanf("%d%d%d",&u,&v,&c);
            addedge(u,v,c);
            union_find(u,v);
        }
        return true;
    }
    void dfs(int u,int deep)
    {
        dfn[u]=++maxnode;ver[maxnode]=u;depth[maxnode]=deep;
        for(int i=head[u];~i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(dfn[v]) continue;
            dis[v]=dis[u]+edge[i].val;
            dfs(v,deep+1);
            depth[++maxnode]=deep;ver[maxnode]=u;
        }
    }
    const int maxdepth=20;
    int dp[maxn<<1][maxdepth];
    void ST(int n)// n=::maxnode
    {
        for(int i=1;i<=n;i++) dp[i][0]=i;
        for(int j=1;(1<<j)<=n;j++)//careful of the limits
            for(int i=1;i+(1<<j)-1<=n;i++)//careful of the limits by y=dp[i+(1<<j-1)][j-1] and the limit of i+(1<<j)-1<=n, must -1 'coz the i+(1<<j)-1 is possible to be n!!
            {
                int x=dp[i][j-1],y=dp[i+(1<<j-1)][j-1];
                dp[i][j]=depth[x]<depth[y]?x:y;
            }
    }
    inline int RMQ(int l,int r)
    {
        int k=0;
        while(1<<(k+1)<=r-l+1) k++;// careful of the limits!!
        int x=dp[l][k],y=dp[r-(1<<k)+1][k];
        return depth[x]<depth[y]?x:y;
    }
    inline int LCA(int u,int v)
    {
        int x=dfn[u],y=dfn[v];
        if(x>y) swap(x,y);
        int root=RMQ(x,y);
        return ver[root];
    }
    int main()
    {
        freopen("city.in","r",stdin);
        freopen("city.out","w",stdout);
        while(init())
        {
            for(int i=1;i<=n;i++)
                if(!dfn[i]) dfs(i,1);
            ST(maxnode);
            while(q--)
            {
                int x,y;
                scanf("%d%d",&x,&y);
                if(find(x)^find(y)) printf("Not connected
    ");
                else printf("%d
    ",dis[x]+dis[y]-(dis[LCA(x,y)]<<1));
            }
        }
        return 0;
    }
    View Code
  • 相关阅读:
    练习5.6.3节
    size_t
    练习3.43
    use include to read a file
    ACM数学(转)
    POJ 2039 To and Fro
    poj 1716 差分约束
    poj 3159 差分约束
    hdu 4571 floyd+动态规划
    poj 1364 差分约束
  • 原文地址:https://www.cnblogs.com/ourfutr2330/p/5666874.html
Copyright © 2011-2022 走看看