zoukankan      html  css  js  c++  java
  • 1714:地壳运动

    1714:地壳运动

    时间限制: 6000 ms         内存限制: 131072 KB

    【题目描述】

    城市中建立了N
    个应急避难所以躲避灾害,这些避难所从1~N编号。此外有M条道路连接这些避难所,所有避难所间均可通过这M条道路直接或间接到达。路可以由若干个平行于x或y坐标轴的线段组成,所以避难所xi和yi之间的道路可以用(ui,vi)来表示,道路的长度为ui+vi。

    由于地壳运动会导致地面拉伸或收缩,可用两个实数k1,k2描述城市的伸缩程度,此时某条道路的实际长度变为ui×k1+vi×k2。

    有若干个独立的询问,每次询问给出k1和k2,

    政府都希望在此地壳运动前提下,以最小的花费维护其中一些道路,使得只用这些被维护的道路仍能使所有避难所间相互连通。

    因为花费与道路的实际总长成正比,所以你需要对每一次询问求出被维护道路的最短实际总长度。
    【输入】

    第一行三个整数N,M,Q,分别表示避难所数量、道路数量、询问数量。

    接下来M行每行四个整数xi,yi,ui,vi。xi,yi表示道路连接的两个避难所编号,ui,vi

    意义如上文所述。

    最后Q行每行两个实数,表示每次询问的的k1和k2。
    【输出】

    输出Q行,每行一个实数,表示实际总长度,保留三位小数。
    【输入样例】

    4 8 3
    2 1 3 6
    3 2 0 7
    4 1 7 0
    1 4 4 6
    2 1 2 7
    1 2 2 10
    2 2 5 5
    4 4 8 9
    0.626436771146 0.472537839745
    0.977631137354 0.190235819672
    0.418883351791 0.221987861358

    【输出样例】

    12.253
    9.671
    6.878

    【提示】

    【数据规模及约定】

    对于30%的数据,N≤30,M≤3000,Q≤3000;

    对于另外30%的数据,N≤20,M≤25000,Q≤10000;

    对于100%的数据,N≤35,M≤25000,Q≤200000,1≤xi,yi≤N,0≤ui,vi≤106,k1,k2>0。

     

    【题解】

     

    显然我们每次询问需要跑一个prim求最小生成树。那么我们的任务就是快速求出此次询问时每条边选用哪个。

     

    可以将x,y相同的所有边放入一个vector中分别处理,考虑最优的边就是min(ui*k1+vi*k2)。

     

    设该边权值为W=ui*k1+vi*k2。

     

    vi=(k1*ui-W)/k2。

     

    vi=(k1/k2)*ui-W/k2。

     

    按u升序排列,相同u去v最大值,这就可以使用斜率优化实现处理出对于斜率最优的边。

     

    将询问按斜率排序,即可快速查询。

     

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    const int N=36;
    int n,m,q,siz[N][N],dao[N][N];
    double ma[N][N],dis[N],ans[200005];
    bool book[N];
    struct point
    {
        int u,v;
        point(int x=0,int y=0)
        {
            u=x;v=y;
        }
    };
    struct xunwen
    {
        double k1,k2;
        int id;
    }wen[200005];
    vector <point> ve[N][N],st[N][N];
    inline bool cmp(point x,point y)
    {
        return x.u<y.u;
    }
    inline bool cmp2(xunwen x,xunwen y)
    {
        return -(x.k1/x.k2)<-(y.k1/y.k2);
    }
    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 double query(xunwen x)
    {
        double xlv=x.k1/x.k2;
        for(int i=1;i<n;i++)
            for(int j=i+1;j<=n;j++)
                if(siz[i][j])
                {
                    int h=dao[i][j];
                    while(dao[i][j]<=siz[i][j]-2&&-xlv>1.0*(st[i][j][h+1].v-st[i][j][h].v)/(st[i][j][h+1].u-st[i][j][h].u))
                    {
                        h++;dao[i][j]++;
                    }
                    ma[i][j]=ma[j][i]=x.k1*st[i][j][h].u+x.k2*st[i][j][h].v;
                }
                else ma[i][j]=ma[j][i]=999999999;
        memset(book,0,sizeof(book));
        dis[1]=0;double daan=0;
        for(int i=2;i<=n;i++) dis[i]=ma[1][i];
        for(int i=1;i<=n-1;i++)
        {
            double mx=999999999;
            int zai;
            for(int j=2;j<=n;j++)
                if(!book[j]&&dis[j]<mx) 
                    mx=dis[j],zai=j;
            book[zai]=1;daan+=dis[zai];
            for(int j=2;j<=n;j++)
                if(!book[j])
                    dis[j]=min(dis[j],ma[zai][j]);
        }
        return daan;
    }
    int main()
    {
        n=read();m=read();q=read();
        for(int i=1,x,y,u,v;i<=m;i++)
        {
            x=read();y=read();u=read();v=read();
            if(x>y) swap(x,y);
            ve[x][y].push_back(point(u,v));
        }
        for(int i=1;i<n;i++)
            for(int j=i+1;j<=n;j++)
                if(!ve[i][j].empty())
                {
                    int len=ve[i][j].size();
                    sort(ve[i][j].begin(),ve[i][j].end(),cmp);
                    for(int k=0;k<len;k++)
                    {
                        if(k>=1&&ve[i][j][k].u==st[i][j][siz[i][j]-1].u)
                        {
                            if(ve[i][j][k].v>=st[i][j][siz[i][j]-1].v) continue;
                            else st[i][j].pop_back(),siz[i][j]--;
                        }
                        int h=siz[i][j];
                        while(siz[i][j]>=2&&1ll*(st[i][j][h-2].v-st[i][j][h-1].v)*(st[i][j][h-1].u-ve[i][j][k].u)
                        >1ll*(st[i][j][h-2].u-st[i][j][h-1].u)*(st[i][j][h-1].v-ve[i][j][k].v)) h--,siz[i][j]--,st[i][j].pop_back();
                        siz[i][j]++;
                        st[i][j].push_back(ve[i][j][k]);
                    }
                }
        for(int i=1;i<=q;i++)
        {
            scanf("%lf%lf",&wen[i].k1,&wen[i].k2);
            wen[i].id=i;
        }
        sort(wen+1,wen+q+1,cmp2);
        for(int i=1;i<=q;i++)
        {
            ans[wen[i].id]=query(wen[i]);
        }
        for(int i=1;i<=q;i++) printf("%.3f
    ",ans[i]);
    }
    View Code
  • 相关阅读:
    linux中的信号机制
    函数指针读书笔记
    const读书笔记
    动态规划----0/1背包问题
    函数指针的用法---以冒泡排序为例
    各种排序算法的实现(更新中)
    Flutter滚动型容器组件
    Flutter json转实体类(插件自动生成)
    Flutter 键盘弹出背景图片变形
    Flutter BottomNavigationBar切换页面被重置问题(保存状态)
  • 原文地址:https://www.cnblogs.com/betablewaloot/p/12207911.html
Copyright © 2011-2022 走看看