【NOIP2014八校联考第3场第2试10.5】地壳运动(mst)

Description

JZ是一个坐落在地壳运动活跃的山区的城市,常受地质灾害的袭击。
城市中建立了N个应急避难所以躲避灾害,这些避难所从1~N编号。此外有M条道路连接这些避难所,所有避难所间均可通过这M条道路直接或间接到达。由于是在规划良好的市区,道路可以由若干个平行于x或y坐标轴的线段组成,所以避难所xi和yi之间的道路可以用(ui,vi)来表示,道路的长度为ui+vi。由于地壳运动会导致地面拉伸或收缩,可用两个实数k1,k2描述城市的伸缩程度,此时某条道路的实际长度变为ui*k1+vi*k2。有若干个独立的询问,每次询问给出k1和k2,政府都希望在此地壳运动前提下,以最小的花费维护其中一些道路,使得只用这些被维护的道路仍能使所有避难所间相互连通。因为花费与道路的实际总长成正比,所以你需要对每一次询问求出被维护道路的最短实际总长。

Solution

这题就是二维的最小生成树,那么就是最小乘积生成树。
之前打过一次,这一次来复习一下。
首先对于(k1,k2)放入数组中做最小生成树之后得出的(x,y),k1和k2显然要一增一减那么就是一个下凸壳。
首先我们找出这个下凸壳的两段k1=1,k2=0和k2=0,k1=1,用mst分别求出这两个点(用mst求出最小生成树上u值的和,和v值的和),然后我们考虑分治求出整个下凸壳。
【NOIP2014八校联考第3场第2试10.5】地壳运动(mst)_第1张图片
A和B是下凸壳的两个端点,那么我们现在要找一个C距离AB最远,那么就相当于找一个点使得S△ABC最大,用叉积搞了一波,C点的k1=abs(y2-y1),k2=abs(x2-x1)(推起来有点复杂,一般二维的最小生成树都是这个),然后在用mst就出C的点,然后向两边分治下去,知道分治出的C点在AB上,那么就说明当前AB的左下角已经无法更优了。
那么把这个凸包上的点放进一个数组里面去。
然后询问k1,k2,那么因为是一个凸壳,所以类似二次函数求极值一样用三分来做就好了。

Code

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define rep(i,a) for(i=tou[a];i;i=next[i])
using namespace std;
typedef double db;
const int maxn=100007;
int i,j,k,t,n,m,q,x,y,cnt;
int tou[40],c[maxn],tot,f[40],sum,op,l,r,mid1,mid2;
db k1,k2,ans;
struct node{
    int a,b;db u,v,o;
}a[maxn];
struct nod{
    db k1,k2,a,b;
}head,tail,b[maxn];
bool cmp(node x,node y){return x.u*k1+x.v*k2<y.u*k1+y.v*k2;}
int gf(int x){
    if(!f[x])return x;
    f[x]=gf(f[x]);
    return f[x];
}
void gao(nod &x){
    k1=x.k1,k2=x.k2;
    int i,k=0,b,c;
    x.a=x.b=0;
//  fo(i,1,m)a[i].o=a[i].u*k1+a[i].v*k2;
    sort(a+1,a+1+m,cmp);memset(f,0,sizeof(f));
    fo(i,1,m){
        b=gf(a[i].a),c=gf(a[i].b);
        if(b!=c){
            f[b]=c;
            x.a+=a[i].u;
            x.b+=a[i].v;
            k++;
        }
        if(k==n-1)break;
    }
}
void dfs(nod l,nod r)
{
    nod mid;
    mid.k1=fabs(l.b-r.b),mid.k2=fabs(l.a-r.a);
    gao(mid);
    if(l.b==r.b||l.b==mid.b||(l.a-r.a)/(l.b-r.b)==(l.a-mid.a)/(l.b-mid.b))return;
    dfs(l,mid);
    b[++op]=mid;
    dfs(mid,r);
}
int main(){
//  freopen("fan.in","r",stdin);
//  freopen("fan.out","w",stdout);
    scanf("%d%d%d",&n,&m,&q);
    fo(i,1,m){
        scanf("%d%d%lf%lf",&a[i].a,&a[i].b,&a[i].u,&a[i].v);
        if(a[i].a>a[i].b)swap(a[i].a,a[i].b);
    }
    head.k1=1,head.k2=0;
    tail.k1=0,tail.k2=1;
    gao(head);
    gao(tail);
    b[op=1]=head;
    dfs(head,tail);
    b[++op]=tail;
    fo(i,1,q){
        scanf("%lf%lf",&k1,&k2);
        if(i==94){
            ans=ans;
        }
        ans=99999999999999;
        l=1,r=op;
        while(l2){
                break;
            }
            else{
                mid1=l+(r-l+1)/3;
                mid2=l+(r-l+1)/3*2;
            }
        //  if(mid2==r)mid2--;
            if(b[mid1].a*k1+b[mid1].b*k2.a*k1+b[mid2].b*k2)r=mid2;else l=mid1;
        }
        fo(j,l,r)ans=min(ans,b[j].a*k1+b[j].b*k2);
        printf("%.3lf\n",ans);
    }
}

你可能感兴趣的:(NOIP,地壳运动,最小乘积生成树,最小生成树,凸包,noip,最小生成树,三分)