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]); }