【51NOD1112KGold】 二分+优先队列

51NOD1112KGold
题意就是给你n个直线,求这些直线在第一象限的前10000个交点。
这道题由于数据量有10000,所以很难下手,如果是n^2枚举+优先队列会MLE,如果每次维护一个10000大小的优先队列又会TLE,所以我们需要二分出这前一万个点在哪个横坐标之前,二分的时候要先将直线按照b从小到大排序,之后如果我们枚举当前横坐标为 x 0 x_0 x0,按照 k ∗ x 0 + b k*x_0+b kx0+b,每个直线的新下标pos如果大于原来的下标prepos,那么表示这条直线与之前的prepos-pos条直线有交点,这样统计出当前x左侧的交点个数,按照交点个数是否超过10000进行二分,统计出横坐标 X 1 X_1 X1之后,我们再 n 2 n^2 n2枚举交点将在 0 − X 1 0-X_1 0X1中的交点信息丢进优先队列,最后输出前一万个就可以。

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<queue>
using namespace std;
#define dbg(x1,x2) cout<<#x1<<" = "<<x1<<" "<<#x2<<" = "<<x2<<endl
const int maxn = 1e4+10;
const double eps=1e-10;
struct data
{
    int k,b,id;
}p[maxn];
bool cmp3(const data &a,const data &b)
{
    return a.b<b.b;
}
struct node
{
    int id;
    double val;
}p2[maxn];
bool cmp(const node &a,const node &b)
{
    return a.val<b.val;
}
bool eql(double a,double b)
{
    if(fabs(a-b)<eps) return true;
    return false;
}
struct pp
{
    double x;
    int id_1,id_2;
    pp(double xx,int id,int id2)
    {
        x=xx;
        id_1=id;
        id_2=id2;
    }
    friend operator <(pp a,pp b)
    {
        if(eql(a.x,b.x))
        {
            if(a.id_1==b.id_1)
            {
                return a.id_2>b.id_2;
            }
            return a.id_1>b.id_1;
        }
        return a.x>b.x;
    }
};
int n,X;
int cal(double mid)
{
    int ans=0;
    X=mid;
    for(int i=1;i<=n;i++)
    {
        p2[i].val=p[i].k*X+p[i].b;
        p2[i].id=i;
    }
    sort(p2+1,p2+1+n,cmp);
    for(int i=1;i<=n;i++)
    {
        if(i<p2[i].id) ans+=p2[i].id-i;
    }
    return ans;
}
priority_queue<pp> pq;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&p[i].b,&p[i].k);
        p[i].id=i;
    }
    sort(p+1,p+1+n,cmp3);
    double l=0.0,r=1e18;
    while(r-l>=eps)
    {
        double mid=(l+r)/2.0;
        if(cal(mid)==10000)
        {
            l=mid;
            break;
        }
        if(cal(mid)>=10000)  r=mid-1;
        else  l=mid+1;
    }
    for(int i=2;i<=n;i++)
    {
        for(int j=1;j<i;j++)
        {
            double tmp=(1.0*(p[i].b-p[j].b)/(1.0*(p[j].k-p[i].k)));
            if(tmp>0&&tmp<=l)
            {
                pq.push(pp(tmp,p[j].id,p[i].id));
            }
        }
    }
    int cnt=0;
    while(cnt<10000&&!pq.empty())
    {
        pp tt=pq.top();
        pq.pop();
        printf("%d %d\n",tt.id_1,tt.id_2);
        cnt++;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值