zoukankan      html  css  js  c++  java
  • floj 2264

    2公路建设(highway.c/cpp/pas)

    滨海一共有n 个城市,编号依次为1n,它们之间计划修建m条双向道路,其中修建第i条道路的费用为ci

    海霸王作为滨海公路建设项目的总工程师,他决定选定一个区间[l,r],仅使用编号在该区间内的道路。他希望选择一些道路去修建,使得连通块的个数尽量少,同时,他不喜欢修建多余的道路,因此每个连通块都可以看成一棵树的结构。为了选出最佳的区间,海霸王会不断选择q个区间,请写一个程序,帮助海霸王计算每个区间内修建公路的最小总费用。

    Input

    第一行包含三个正整数n,m,q,表示城市数、道路数和询问数。接下来m行,每行三个正整数ui,vi,ci,表示一条连接城市uivi的双向道路,费用为ci接下来q行,每行两个正整数li,ri,表示一个询问。

    Output输出q行,每行一个整数,即最小总费用。

    Notes

    60% 从ci=i。

    100% 的数据,1 ui,vin<=100,uivi1lirim,1ci106

    【题解】

    考虑部分分,边的权值单调递增。

    直接倒着扫所有边,加入该边时如果还未连通就直接加,否则找到环上最大的边删去换成这条,将查询按左端点从大到小排序,扫到左端点相同时查询现在树上所有边是否在区间内,是就将答案加它。

    正解

    发现n很小,可以使用线段树,叶子结点表示每条路,区间查询[l,r]即可。

    每个节点维护一个vector表示这个区间内生成的最小生成森林选了那些边。

    合并时使用归并排序,将2个区间的边排序后跑克鲁斯卡尔即可。

    复杂度m*n+q*logm*n。

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int M=1e5+5;
    int n,m,q,fa[105],b[M];
    struct bian
    {
        int u,v,c,id;
    }a[M];
    vector <int> ve[M<<2],ddhu,ans;
    inline int find(int x)
    {
        if(x==fa[x]) return x;
        fa[x]=find(fa[x]);
        return fa[x];
    }
    inline int read()
    {
        char c=getchar();
        int x=0,f=1;
        while(!isdigit(c)) {if(c=='-') f=-1;c=getchar();}
        while(isdigit(c)) {x=(x<<3)+(x<<1)+c-'0';c=getchar();}
        return x*f;
    }
    inline void pushup(int x,int y)
    {
        ddhu.clear();
        int cnt=0,zi=0,yi=0,zlen=ve[x].size(),ylen=ve[y].size();
        while(zi<zlen&&yi<ylen)
        {
            if(a[ve[x][zi]].c<a[ve[y][yi]].c) b[++cnt]=ve[x][zi++];
            else b[++cnt]=ve[y][yi++];
        }
        while(zi<zlen) b[++cnt]=ve[x][zi++];
        while(yi<ylen) b[++cnt]=ve[y][yi++];
        for(int i=1;i<=n;i++) fa[i]=i;
        for(int i=1;i<=cnt;i++)
        {
            int fa1=find(a[b[i]].u);
            int fa2=find(a[b[i]].v);
            if(fa1!=fa2)
            {
                fa[fa1]=fa2;
                ddhu.push_back(b[i]);
            }
        }
    }
    
    inline void build(int now,int l,int r)
    {
        if(l==r)
        {
            ve[now].push_back(l);
            return;
        }
        int mid=(l+r)>>1;
        build(now<<1,l,mid);
        build(now<<1|1,mid+1,r);
        pushup(now<<1,now<<1|1);
        ve[now].clear();
        int gu=ddhu.size();
        for(int i=0;i<gu;i++) ve[now].push_back(ddhu[i]);
    }
    inline void query(int now,int l,int r,int L,int R)
    {
        if(l>=L&&r<=R)
        {
            int huan=ans.size();
            ve[(M<<2)-2].clear();
            for(int i=0;i<huan;i++) ve[(M<<2)-2].push_back(ans[i]);
            pushup((M<<2)-2,now);huan=ddhu.size();ans.clear();
            for(int i=0;i<huan;i++) ans.push_back(ddhu[i]);
            return;
        }
        int mid=(l+r)>>1;
        if(L<=mid) query(now<<1,l,mid,L,R);
        if(R>=mid+1) query(now<<1|1,mid+1,r,L,R);
    } 
    int main()
    {
    //    freopen("highway.in","r",stdin);
    //    freopen("highway.out","w",stdout);
        n=read();m=read();q=read();
        for(int i=1;i<=m;i++)
            a[i].u=read(),a[i].v=read(),a[i].c=read(); 
        build(1,1,m);
        for(int i=1,x,y;i<=q;i++)
        {
            x=read();y=read();
            ans.clear();
            query(1,1,m,x,y);
            int daan=0,huan=ans.size();
            for(int j=0;j<huan;j++) daan+=a[ans[j]].c;
            cout<<daan<<"
    ";
        }
        fclose(stdin);
        fclose(stdout);
    } 
    
    //3 5 2
    //1 3 2
    //2 3 1 
    //2 1 6
    //3 1 7
    //2 3 7
    //2 5 
    //3 4
    View Code
  • 相关阅读:
    zookeeper(四):核心原理(Watcher、事件和状态)
    zookeeper(三):java操作zookeeper
    Java并发编程(三):并发模拟(工具和Java代码介绍)
    Java并发编程(二):JAVA内存模型与同步规则
    Java并发编程(一):并发与高并发等基础概念
    zookeeper(一):功能和原理
    Sql 获取向上取整、向下取整、四舍五入取整的实例(转)
    gRPC详解
    Google Protobuf简明教程
    nginx error_page配置
  • 原文地址:https://www.cnblogs.com/betablewaloot/p/12181517.html
Copyright © 2011-2022 走看看