zoukankan      html  css  js  c++  java
  • [CQOI2012]组装(贪心+数学)

    传送门

    题意:有n种零件,m个生产车间,给出每个车间的坐标xi和生产零件的种类pi,求组装车间的坐标,使得dis(1)+dis(2)+...+dis(n)最小,其中dis(x)表示生产第x种零件的生产车间到组装车间距离的平方的最小值.

    分析:不难想到,我们最后的最优答案一定是从所有m 个生产车间中选出了n个,使得一个生产车间恰好生产一种零件,这样一定是最优的.(贪心策略)

    设组装车间的坐标为x,选出的n个生产车间坐标为a1,a2...an,则有:

    (ans=sum_{i=1}^n(x-a[i])^2)

    把完全平方式拆开可得:

    (ans=sum n*x^2 - 2sum_{i=1}^na[i]*x+sum_{i=1}^n a[i]^2)

    根据上式,我们只要枚举x(组装车间),同时维护(sum_{i=1}^na[i])(sum_{i=1}^n a[i]^2)这两个值就好了.

    考虑如何枚举和维护?我们先假设组装车间x在所有生产车间的左边,则对于每种零件的生产车间,其对答案产生贡献的一定是最左边的那个生产车间.于是我们可以预处理出此时的答案,然后组装车间不断向右挪.

    考虑组装车间不断向右挪的过程中,每种零件对答案产生贡献的生产车间如何变化?显然,对于同种零件的相邻两个生产车间,当组装车间越过它们的中点时,则对答案产生贡献的生产车间将会发生变化.

    于是我们可以开一个结构体,按照同种零件相邻两个生产车间的中点坐标从小到大,存下转移前后的两个车间的坐标(这一小段,我知道我没有表述清楚,看不懂就直接看代码吧)

    int n,m,tot;
    long long x1,x2;//十年一场空,博主建议都开ll和db
    double anspos,ans;
    vector<int> b[20005];
    struct battle{
        int x,p;
    }a[200005];//记录生产车间坐标和生产零件种类的结构体
    bool cmp(battle a,battle b){
        return a.x<b.x;
    }
    struct Battle{
        int Old,New;
        double mid;
    }c[200005];//记录每一次转移的结构体
    bool Cmp(Battle a,Battle b){
        return a.mid<b.mid;
    }
    void add(int Old,int New){
        c[++tot].Old=Old;
        c[tot].New=New;
        c[tot].mid=(Old+New)*1.0/2;
    }
    double Get(double x){return n*x*x-2*x1*x+x2;}
    void turn(int Old,int New){
        x1-=Old;x1+=New;
        x2-=1LL*Old*Old;x2+=1LL*New*New;
    }//类似于莫队的转移
    int main(){
        n=read();m=read();//n种零件,m个生产车间
        for(int i=1;i<=m;i++){
    		a[i].x=read();//坐标
    		a[i].p=read();//生产零件的种类
        }
        sort(a+1,a+m+1,cmp);//按照坐标从小到大排序
        for(int i=1;i<=m;i++)
    		b[a[i].p].push_back(a[i].x);
    //此时每种零件的生产车间的坐标从小到大递增,存入数组b
        for(int i=1;i<=n;i++)
    		for(int j=1;j<=b[i].size()-1;j++)
    	    	add(b[i][j-1],b[i][j]);
    //每一次存入同一种零件的相邻两个生产车间,并记录中点
        sort(c+1,c+tot+1,Cmp);//按照中点坐标从小到大
        for(int i=1;i<=n;i++){
    		x1+=b[i][0];
    		x2+=b[i][0]*b[i][0];
        }
    //先假设组装车间在所有生产车间的左边
    //所以计算每种零件最左边的生产车间对此时答案的贡献
        anspos=x1*1.0/n;ans=Get(anspos);
    //anspos组装车间的坐标,等于所有生产车间坐标的平均值
    //得到组装车间的坐标后,ans就可以直接用公式计算
        for(int i=1;i<=tot;i++){
    		turn(c[i].Old,c[i].New);
    		if(Get(x1*1.0/n)<ans){//更新最小值
    	    	ans=Get(x1*1.0/n);
    	    	anspos=x1*1.0/n;
    		}
        }
        printf("%.4lf
    ",anspos);
        return 0;
    }
    
    
  • 相关阅读:
    ADODB.Recordset错误800a0cb3"的解决方
    C++ primer第一章 C++概述 纪要
    [收藏]一些电平转换方法
    “当传递具有已修改行的 DataRow 集合时,更新要求有效的 UpdateCommand”错误解决
    关于句柄的一些知识 Handle (computing) Wiki
    “/”应用程序中的服务器错误。当前标识(NT AUTHORITY/NETWORK SERVICE)没有对“C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/Temporary ASP.NET Files”的写访问权
    C++中 __stdcall,__cdecl, __fastcal区别简介
    [How To]使用Wildfish进行开发新Solution配置篇
    关于ISeries的代码生成器
    [How To]如何使用Wildfish进行ISeries项目开发数据库建立部分
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10341554.html
Copyright © 2011-2022 走看看