zoukankan      html  css  js  c++  java
  • 【NOIP】货车运输(最大生成树+树链剖分)

    题意

    给你一个无向图(可能不连通,可能有重边),每条边有个限重,货车运输的时候不能超过这个限重,现在问对于一个起点和终点,问货车最多可以运多少货物。


    思路

    这道题就是让我们求一个瓶颈路,并且这个瓶颈路一定在最大生成森林上面,用反证法可以知道不在最大生成森林上面的一定是更劣的答案。
    所以在最大生成森林上面跑树剖就ok了。


    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define root 1,dfs_clock,1
    #define ls l,m,o<<1
    #define rs m+1,r,o<<1|1
    inline void read(int& x){
        char c=getchar();int p=1,n=0;
        while(c<'0'||c>'9'){if(c=='-')p=-1;c=getchar();}
        while(c>='0'&&c<='9'){n=n*10+c-'0';c=getchar();}
        x=p*n;
    }
    const int maxn=10000+10,maxm=50000*2+10,inf=1e9;
    struct edge2{
        int v,next,w;
    }a[maxm];
    int h[maxn],tot;
    void add_e(int x,int y,int z){
        a[tot].v=y;
        a[tot].next=h[x];
        a[tot].w=z;
        h[x]=tot++;
    }
    struct edge{
        int u,v,w;
    }b[maxm];
    int n,m;
    inline bool cmp(edge a,edge b){
        return a.w>b.w;
    }
    int f[maxn];
    int find(int x){
        if(f[x]==x)return x;
        return f[x]=find(f[x]);
    }
    inline void merge(int u,int v){
        int x=find(u);
        int y=find(v);
        if(x!=y)f[x]=y;
    }
    
    int bian[maxm];//debug
    
    inline void kruskal(){
        for(int i=1;i<=n;i++)
            f[i]=i;
        memset(h,-1,sizeof h);tot=0;
        for(int i=1;i<=m;i++){
            int u=b[i].u,v=b[i].v;
            if(find(u)!=find(v)){
                bian[i]=1;
                merge(u,v);
                add_e(u,v,b[i].w);
                add_e(v,u,b[i].w);
            }
        }
    }
    int sz[maxn],son[maxn],w[maxn],fa[maxn],dep[maxn];
    int tid[maxn],rank[maxn],top[maxn];
    int jl[maxn],vis[maxn];
    int dfs_clock;
    void dfs1(int u){
        sz[u]=1;son[u]=0;vis[u]=1;
        for(int i=h[u];~i;i=a[i].next){
            int v=a[i].v;
            if(v==fa[u])continue;
            fa[v]=u;dep[v]=dep[u]+1;w[v]=a[i].w;
            dfs1(v);sz[u]+=sz[v];
            if(sz[v]>sz[son[u]])son[u]=v;
        }
    }
    void dfs2(int u,int anc){
        tid[u]=++dfs_clock;rank[tid[u]]=u;top[u]=anc;
        if(son[u]){
            dfs2(son[u],anc);
            for(int i=h[u];~i;i=a[i].next){
                int v=a[i].v;
                if(v==fa[u]||v==son[u])continue;
                dfs2(v,v);
            }
        }
    }
    int ql,qr;
    int seg[maxn*4];
    inline void pushup(int o){
        seg[o]=min(seg[o<<1],seg[o<<1|1]);
    }
    int query(int l,int r,int o){
        if(ql<=l&&r<=qr)
            return seg[o];
        int m=l+(r-l)/2;
        int mi=inf;
        if(ql<=m)mi=min(mi,query(ls));
        if(qr>m)mi=min(mi,query(rs));
        return mi;
    }
    void build(int l,int r,int o){
        if(l==r){
            seg[o]=w[rank[l]];
            return;
        }
        int m=l+(r-l)/2;
        build(ls);
        build(rs);
        pushup(o);
    }
    inline int ask(int x,int y){
        int mi=inf;
        while(top[x]!=top[y]){
            if(dep[top[x]]<dep[top[y]])
                swap(x,y);
            ql=tid[top[x]];qr=tid[x];
            mi=min(mi,query(root));
            x=fa[top[x]];
        }
        if(dep[x]>dep[y])
            swap(x,y);
        ql=tid[x]+1;qr=tid[y];
                //这里为什么tid[x]+1?就是为了去掉w[lca(x,y)]对答案的影响。
        mi=min(mi,query(root));
        return mi;
    }
    void debug(){
        for(int i=1;i<=m;i++)
            if(bian[i])
                printf("%d %d %d
    ",b[i].u,b[i].v,b[i].w);
    }
    int main(){
    //  freopen("货车运输.in","r",stdin);
        read(n);read(m);
        for(int i=1;i<=m;i++){
            read(b[i].u);read(b[i].v);read(b[i].w);
        }
        sort(b+1,b+1+m,cmp);
        kruskal();
    
    //  debug();
    
        int cnt=0;
        for(int i=1;i<=n;i++)
            if(!vis[i]){
                w[i]=inf;
                jl[++cnt]=i;//原图有可能是森林。
                dfs1(i);
            }
        dfs_clock=0;
        for(int i=1;i<=cnt;i++)
            dfs2(jl[i],jl[i]);//对于每一棵树,都要拉一遍重链。
        build(root);
        int q;read(q);
        for(int i=1;i<=q;i++){
            int x,y;
            read(x);read(y);
            if(find(x)!=find(y))
                printf("-1
    ");
            else
                printf("%d
    ",ask(x,y));
        }
        return 0;
    }

    后记

    边权下放又打错了一次,记得w[lca(x,y)]是不能放进线段树考虑的!

  • 相关阅读:
    sp2010 升级sp2013 用户无法打开网站
    powerviot install in sharepoint 2013
    can not connect cube in performancce dashboard
    westrac server security configure user info
    添加报表服务在多服务器场
    sharepoint 2013 office web app 2013 文档在线浏览 IE11 浏览器不兼容解决方法
    delete job definition
    目前付款申请单内网打开慢的问题
    item style edit in sharepoint 2013
    Could not load file or assembly '$SharePoint.Project.AssemblyFullName$'
  • 原文地址:https://www.cnblogs.com/yohanlong/p/6058144.html
Copyright © 2011-2022 走看看