[luogu4479][BJWC2018]第k大斜率【二维偏序+二分+离散化+树状数组】

传送门

https://www.luogu.org/problemnew/show/P4479

题目描述

在平面直角坐标系上,有 n 个不同的点。任意两个不同的点确定了一条直线。请求出所有斜率存在的直线按斜率从大到小排序后,第 k 条直线的斜率为多少。
为了避免精度误差,请输出斜率向下取整后的结果。(例如: ?1.5? = 1 , ??1.5? = ?2 )

分析

一开始打了一个暴力,10分后来改着改着成了30分,浮点误差。
正解其实很简单,我们首先逆向思考一下,如果我们假设已经有了斜率k。
如果两个点之间需要斜率大于k,并且假设我们已经排好了关于x横坐标的序(递增),那么一定会有一个\(\frac{y_j-y_i}{x_j-x_i}>k\)
那么进一步拆开式子最终得到\(y_j-kx_j>y_j-kx_j\),那么就变成了一个二维偏序,那么离散化+二分k+树状数组求解二维偏序就可以了。

ac代码

#include <bits/stdc++.h>
#define ll long long
#define db double
#define ms(a, b) memset(a, b, sizeof(a))
#define inf 0x3f3f3f3f
using namespace std;
template <typename T>
inline void read(T &x) {
    x = 0; T fl = 1;
    char ch = 0;
    while (ch < '0' || ch > '9') {
        if (ch == '-') fl = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    x *= fl;
}
#define N 100005
struct data {
    ll x, y, id, val;
    bool operator <(const data &rhs) const {
        return val == rhs.val ? x < rhs.x : val < rhs.val;
    }
}a[N];
struct BIT {
    #define lowbit(x) (x&-x)
    ll tr[N];
    int n;
    void init(int nn) {
        ms(tr, 0);
        n = nn;
    }
    void add(int x, int val) {
        for (; x <= n; x += lowbit(x)) tr[x] += val;
    }
    ll query(int x) {
        ll res = 0;
        for (; x; x -= lowbit(x)) res += tr[x];
        return res;
    }
}tr;
int n;
int disc_x[N];
ll k;
bool check(int x) {
    for (int i = 1; i <= n; i ++) {
        a[i].val = a[i].y - 1ll * x * a[i].x;
    }
    sort(a + 1, a + 1 + n);
    tr.init(n);
    ll ans = 0;
    for (int i = 1; i <= n; i ++) {
        ans += tr.query(a[i].id - 1);
        tr.add(a[i].id, 1);
    }
    return ans >= k;
}
int main() {
    read(n); read(k);
    for (int i = 1; i <= n; i ++) {
        read(a[i].x); read(a[i].y);
        disc_x[i] = a[i].x;
    }
    sort(disc_x + 1, disc_x + 1 + n);
    int disc_tot = unique(disc_x + 1, disc_x + 1 + n) - disc_x - 1;
    for (int i = 1; i <= n; i ++) {
        a[i].id = lower_bound(disc_x + 1, disc_x + 1 + disc_tot, a[i].x) - disc_x;
    }
    int l = -inf, r = inf, ans;
    while (l <= r) {
        int mid = (l + r) >> 1;
        if (check(mid)) l = mid + 1, ans = mid;
        else r = mid - 1;
    }
    printf("%d\n", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/chhokmah/p/10575361.html

时间: 2024-12-15 23:30:59

[luogu4479][BJWC2018]第k大斜率【二维偏序+二分+离散化+树状数组】的相关文章

【poj1901-求区间第k大值(带修改)】树状数组套主席树

901: Zju2112 Dynamic Rankings Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 7025  Solved: 2925[Submit][Status][Discuss] Description 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[i+2]--a[j]中第k小的数是多少(1≤k≤j-i+1),并且,你可以改变一些a[i]

Codeforces Round #629 (Div. 3) F - Make k Equal (离散化 树状数组维护前缀和)

https://codeforces.com/contest/1328/problem/F 首先把a数组处理成pair对(num,cnt),表示数字num有cnt个,然后按num升序排序离散化一下. 对于一个数x,若想使得小于x的数字都变成x,必须先把所有小于x的数变成x-1,然后再+1变成x. 同理,要使得大于x的数变成x,必须把所有大于x的数字变成x+1,然后再-1变成x. 以上是题意所要求的必须操作. 思路: 1. 用f[i]数组记录离散化后前i大的数字的总数,那么对于任意第i大数字,可以

hdu-3584 Cube---三维树状数组+区域更新单点查询

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3584 题目大意: 给定一个N*N*N多维数据集A,其元素是0或是1.A[i,j,k]表示集合中第 i 行,第 j 列与第 k 层的值. 首先由A[i,j,k] = 0(1 <= i,j,k <= N). 给定两个操作: 1:改变A[i,j,k]为!A[i,j,k]. 2:查询A[i,j,k]的值. 解题思路: 三维树状数组模拟,利用容斥原理 1 #include<cstdio> 2

bzoj 5163: 第k大斜率

5163: 第k大斜率 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 15  Solved: 4[Submit][Status][Discuss] Description 在平面直角坐标系上,有n个不同的点.任意两个不同的点确定了一条直线.请求出所有斜率存在的直线按斜率从 大到小排序后,第k条直线的斜率为多少.为了避免精度误差,请输出斜率向下取整后的结果.(例如:[1.5]=1, [-1.5]=-2) Input 第一行,包含两个正整数n和k.

HDU_4456_二维树状数组

http://acm.hdu.edu.cn/showproblem.php?pid=4456 第一道二维树状数组就这么麻烦,题目要计算的是一个菱形范围内的和,于是可以把原来的坐标系旋转45度,就是求一个正方形范围内的和,这里还涉及到坐标系平移和放大,由于题目数据较大,用了离散化来保存需要处理的点,放在h数组内,对应的二维树状数组存在tree数组内. #include<iostream> #include<cstring> #include<cstdio> #includ

tyvj P1716 - 上帝造题的七分钟 二维树状数组区间查询及修改 二维线段树

P1716 - 上帝造题的七分钟 From Riatre    Normal (OI)总时限:50s    内存限制:128MB    代码长度限制:64KB 背景 Background 裸体就意味着身体. 描述 Description “第一分钟,X说,要有矩阵,于是便有了一个里面写满了0的n×m矩阵.第二分钟,L说,要能修改,于是便有了将左上角为(a,b),右下角为(c,d)的一个矩形区域内的全部数字加上一个值的操作.第三分钟,k说,要能查询,于是便有了求给定矩形区域内的全部数字和的操作.第

二维树状数组复习—— SuperBrother打鼹鼠

在这个“打鼹鼠”的游戏中,鼹鼠会不时地从洞中钻出来,不过不会从洞口钻进去(鼹鼠真胆大……).洞口都在一个大小为n(n< =1024)的正方形中.这个正方形在一个平面直角坐标系中,左下角为(0,0),右上角为(n-1,n-1).洞口所在的位置都是整点,就是横纵坐标都为整数的点.而SuperBrother也不时地会想知道某一个范围的鼹鼠总数.这就是你的任务. 输入 每个输入文件有多行. 第一行,一个数n,表示鼹鼠的范围. 以后每一行开头都有一个数m,表示不同的操作: m=1,那么后面跟着3个数x,y

[LA7139 Rotation(2014 shanghai onsite)]二维树状数组

题意:有一个n*m的矩形,一辆车从左上角出发,沿一条路径走,路径是由矩形上每个单元格的边构成的,最后回到左上角,求车子在每个格子转过圈数的平方和. 思路:假设需要记录每个格子转的顺时针的圈数(为负表示转的逆时针),可以考虑车子每次移动对各个格子的贡献: 车子左移,路径上方所有格子转的圈数+1,路径下方所有格子-1,而上方和下方所有格子都形成大的矩形,于是相当于每次对矩形区域的格子全部执行加减操作. 车子右移,上方-1,下方+1. 车子上移,左边-1,右边+1. 车子下移,左边+1,右边-1. 对

二维树状数组——SuperBrother打鼹鼠(Vijos1512)

树状数组(BIT)是一个查询和修改复杂度都为log(n)的数据结构,主要用于查询任意两位之间的所有元素之和,其编程简单,很容易被实现.而且可以很容易地扩展到二维.让我们来看一道很裸的二维树状数组题: 在一个“打鼹鼠”的游戏中,鼹鼠会不时地从洞中钻出来,不过不会从洞口钻进去(鼹鼠真胆大……).洞口都在一个大小为n(n<=1024)的正方形中.这个正方形在一个平面直角坐标系中,左下角为(0,0),右上角为(n-1,n-1).洞口所在的位置都是整点,就是横纵坐标都为整数的点.而SuperBrother