POJ

包含一些ai和bi的集用S来表示,x = max(sigma(ai)/sigma(bi),i 属于S) ,k 表示S的大小,k= |S|。

x和k之间具有单调性。k0 < k1 → x0 ≥ x1。单调性对x(k)的反函数k(x)也成立。现在的问题是k已经给出,

那么猜测一个x,通过和sigma(ai)/sigma(bi) 作差得到大小关系以便收敛区间。

要求x可以做一点变形sigma(ai)/sigma(bi) - k*x → sigma(ai - x*b),x是max值,和一个常数作差以后也是最大的,

因此选取前k大元素ai - x*b求和就可以得到差值。

(还有一种很迷的迭代法也可以收敛

/*********************************************************
*            ------------------                          *
*   author AbyssalFish                                   *
**********************************************************/
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<queue>
#include<vector>
#include<stack>
#include<vector>
#include<map>
#include<set>
#include<algorithm>
#include<cmath>
#include<numeric>
using namespace std;

const int maxn = 1e5;
int n, k;

struct Query
{
    int v,w;
    double key;
}q[maxn];

int r[maxn];

struct cmp
{
    bool operator()(int a,int b){ return q[a].key > q[b].key; }
};

double cal(double x)
{
    for(int i = 0; i < n; i++){
        q[r[i]].key =  q[r[i]].v-x*q[r[i]].w;
    }
    nth_element(r,r+k,r+n,cmp());
    double re = 0;
    for(int i = 0; i < k; i++){
        re += q[r[i]].key;
    }
    return re;
}

const double eps = 1e-11;
//#define LOCAL
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif
    while(~scanf("%d%d",&n,&k)){
        double lb = 0, ub = 0, md, re;
        for(int i = 0; i < n; i++){
            scanf("%d%d",&q[i].v,&q[i].w);
            r[i] = i;
            ub = max(ub,(double)q[i].v/q[i].w);
        }

        for(int i = 100; i--;){
            md = (lb+ub)/2;
            re = cal(md);
            if(re > eps) lb = md;
            else if(re < -eps) ub = md;
            else break;
        }
        k--;
        for(int i = 0; i < k; i++){
            printf("%d ",r[i]+1);
        }
        printf("%d
",r[k]+1);
    }

    return 0;
}
原文地址:https://www.cnblogs.com/jerryRey/p/4969716.html