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\n",ans[i]);
}
View Code

猜你喜欢

转载自www.cnblogs.com/betablewaloot/p/12207911.html