AtCoder 294F 二分套二分

题意:

两个人分别有nnnmmm杯糖水,第一个人的第iii杯有a[i]a[i]a[i]克糖,b[i]b[i]b[i]克水,第二个人的第iii杯有c[i]c[i]c[i]克糖,d[i]d[i]d[i]克水。现在从两个人手中各取一杯糖水混合,有nmnmnm种可能的情况,求出其中浓度第kkk大的那一杯的浓度

Solution:

不妨先考虑这样一个问题:

两个数组各取一个元素,乘积有nmnmnm种,求出其中第kkk大的结果

直接二分答案,检查二分的答案在所有的情况排名第几,即有多少(i,j)(i,j)(i,j)
a[i]∗b[j]>mid a[i]*b[j]>mid a[i]b[j]>mid
只需要固定iii,二分符合这个iiijjj的数量即可,单次check的时间复杂度是O(nlogn)O(nlogn)O(nlogn)

这样只需要找到最后一个排名为kkk的数即可,总复杂度O(nlognlogr)O(nlognlogr)O(nlognlogr)rrr是二分答案的长度

按照上述的思路,我们不妨也考虑二分答案

只需要找到有多少(i,j)(i,j)(i,j)
a[i]+c[j]a[i]+b[i]+c[j]+d[j]>mid \frac{a[i]+c[j]}{a[i]+b[i]+c[j]+d[j]}>mid a[i]+b[i]+c[j]+d[j]a[i]+c[j]>mid
注意对一杯糖水并不是加浓度更高的糖水浓度就一定更高

此时把(2)(2)(2)改写成:
a[i]+c[j]>mid(a[i]+b[i]+c[j]+d[j])×mid a[i]+c[j]>mid(a[i]+b[i]+c[j]+d[j])\times mid a[i]+c[j]>mid(a[i]+b[i]+c[j]+d[j])×mid
下标分类,有
(1−mid)×a[i]−mid×b[i]>(mid−1)×c[j]+mid×d[j] (1-mid)\times a[i]-mid\times b[i]>(mid-1)\times c[j]+mid\times d[j] (1mid)×a[i]mid×b[i]>(mid1)×c[j]+mid×d[j]
每次check只需要预先计算右式,排序后枚举iii在右式上二分即可了

#include<iostream>
#include<utility>
#include<vector>
#include<cstdlib>
#include<numeric>
#include<unistd.h>
#include<queue>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<set>
#include<map>
#include<stack>
#include<utility>
#include<cctype>
#include<cassert>
#include<thread>
#include<future>
#include<bitset>
#include<thread>
#include<random>
using namespace std;

using ll=long long;
const int N=5e4+5,inf=0x3fffffff;
const long long INF=0x3fffffffffffffff,mod=1e9+7;

#define double long double

struct sugar {
    int m1,m2;
}a[N],b[N];

int n,m,k;
const double eps=1e-19;

int cmp(double x,double y) {
    if(fabs(x-y)<eps) return 0;
    return x-y>eps?1:-1;
}

double val[N];

int get_rank(double p) {
    for(int i=1;i<=m;i++) val[i]=(p-1)*b[i].m1+p*b[i].m2;
    sort(val+1,val+1+m);

    int ret=0;
    for(int i=1;i<=n;i++) {
        double value=(1-p)*a[i].m1-p*a[i].m2;
        int it=lower_bound(val+1,val+1+m,value)-val;
        ret+=it-1;
    }
    return ret+1;
}

int main() {
    #ifdef stdjudge
        freopen("in.txt","r",stdin);
        auto TimeFlagFirst=clock();
    #endif

    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    cin>>n>>m>>k;
    for(int i=1;i<=n;i++) {
        cin>>a[i].m1>>a[i].m2;
    }
    for(int i=1;i<=m;i++) {
        cin>>b[i].m1>>b[i].m2;
    }

    double l=0,r=1,ans=0;
    for(int i=1;i<=200;i++) {
        double mid=(l+r)/2;
        if(get_rank(mid)<=k) {
            ans=mid;
            r=mid;
        } else l=mid;
    }

    printf("%.15Lf",100*ans);

    #ifdef stdjudge
        freopen("CON","r",stdin);
        std::cout<<std::endl<<"耗时:"<<std::clock()-TimeFlagFirst<<"ms"<<std::endl;
        std::cout<<std::flush;
        system("pause");
    #endif
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值