• [CQOI2012] 组装


    数轴上有 (n) 种点,总共有 (m) 个,每个点有它的坐标 (x_i) 和种类 (p_i)。求一个点,使得所有种类点中与这个点的最小距离的平方和最小。(n le 10^4, m le 10^5, x_i le 10^5)

    Solution

    要最小化 (sum_{i=1}^n (x-x_i)^2),显然在选定了每个种类使用的点以后,这是一个关于 (x) 的二次函数,其最小值为

    [sum_{i=1}^n x_i^2 - frac 1 n sum_{i=1}^n x_i ]

    于是现在我们只需要考虑如何选择每个种类使用的点

    对于同一种点按坐标从小到大排序,每次枚举把某种点替换成他的下一个

    由于原式取得最小值的条件是 (x= frac 1 n sum_{i=1}^n x_i)

    我们将每次替换用一个二元组 ((x_i,x_i')) 表示,那么我们只需要将所有二元组按照 (x_i+x_i') 排序,就一定不会错过最优解

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long 
    const int N = 100005;
    
    int n,m,t1,t2,t3;
    vector <int> g[N];
    struct pii {
        int x,y;
    };
    vector <pii> p;
    double s,s2,ans=1e18,tans=0;
    
    signed main() {
        ios::sync_with_stdio(false);
        cin>>n>>m;
        for(int i=1;i<=m;i++) {
            cin>>t1>>t2;
            g[t2].push_back(t1);
        }
        for(int i=1;i<=n;i++) {
            sort(g[i].begin(),g[i].end());
        }
        for(int i=1;i<=n;i++) {
            if(g[i].size()) {
                s+=g[i][0];
                s2+=g[i][0]*g[i][0];
            }
            for(int j=1;j<g[i].size();j++) {
                p.push_back({g[i][j-1],g[i][j]});
            }
        }
        sort(p.begin(),p.end(),[](pii a,pii b)->bool{return a.x+a.y<b.x+b.y;});
        ans=min(ans,s2-s*s/n);
        tans=s/n;
        for(int i=0;i<p.size();i++) {
            s-=p[i].x;
            s2-=p[i].x*p[i].x;
            s+=p[i].y;
            s2+=p[i].y*p[i].y;
            ans=min(ans,s2-s*s/n);
            if(fabs(s2-s*s/n-ans)<1e-6) tans=s/n;
        }
        printf("%.4lf",tans);
    }
    
    
  • 相关阅读:
    MySQL数据库的完全备份与恢复
    MySQL数据库之索引、事务、存储引擎详细讲解
    LNMP架构介绍与部署
    Haproxy搭建Web集群
    LAMP环境之MySQL服务安装详细过程
    MySQL主从复制详解
    LAMP环境之编译安装httpd服务
    搭建yum软件仓库,让你维护轻松自如
    Shell脚本一键安装Samba服务
    Shell脚本之冒泡排序
  • 原文地址:https://www.cnblogs.com/mollnn/p/12655394.html
Copyright © 2020-2023  润新知