SCUT - 484 - 平面上的点 - 数据结构
一开始想的是按固定斜率的直线从无穷扫下来,但是一直都WA,不知道是哪里错了还是精度问题?
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
int dcmp(long double x, long double y) {
if(fabs(x - y) <= 1e-14)
return 0;
return x < y ? -1 : 1;
}
int n;
ll q;
int ch[MAXN][2];
int val[MAXN];
int dat[MAXN], siz[MAXN], cnt[MAXN];
int tot, root;
inline void Init() {
tot = 0;
root = 0;
}
inline int NewNode(int v) {
val[++tot] = v, dat[tot] = rand();
ch[tot][0] = ch[tot][1] = 0;
siz[tot] = 1, cnt[tot] = 1;
return tot;
}
inline void PushUp(int id) {
siz[id] = siz[ch[id][0]] + siz[ch[id][1]] + cnt[id];
}
inline void Rotate(int &id, int d) {
int temp = ch[id][d ^ 1];
ch[id][d ^ 1] = ch[temp][d];
ch[temp][d] = id;
id = temp;
PushUp(ch[id][d]), PushUp(id);
}
inline void Insert(int &id, int v) {
if(!id)
id = NewNode(v);
else {
if(v == val[id])
++cnt[id];
else {
int d = v < val[id] ? 0 : 1;
Insert(ch[id][d], v);
if(dat[id] < dat[ch[id][d]])
Rotate(id, d ^ 1);
}
PushUp(id);
}
}
//找严格小于v的点的个数
int GetRank(int id, int v) {
if(!id)
return 0;
else {
if(v == val[id])
return siz[ch[id][0]];
else if(v < val[id])
return GetRank(ch[id][0], v);
else
return siz[ch[id][0]] + cnt[id] + GetRank(ch[id][1], v);
}
}
const ll INF = 1e9;
struct Point {
long double x, y;
Point() {}
Point(long double x, long double y): x(x), y(y) {}
Point Rotate(long double A) {
return Point(x * cos(A) - y * sin(A), x * sin(A) + y * cos(A));
}
bool operator<(const Point &p)const {
return (dcmp(x, p.x) != 0) ? (x < p.x) : (y < p.y);
}
} p[MAXN], p2[MAXN];
int nxt[MAXN];
bool check(ll k) {
Init();
long double A = atan2(1.0, 1.0 * k);
for(int i = 1; i <= n; ++i)
p2[i] = p[i].Rotate(A);
//按斜率排序
sort(p2 + 1, p2 + 1 + n);
for(int i = 1; i <= n; ++i) {
p2[i] = p2[i].Rotate(-A);
p2[i].x = round(p2[i].x);
p2[i].y = round(p2[i].y);
}
ll sum = 0;
for(int i = 1; i <= n; ++i) {
int res = GetRank(root, (int)p2[i].x);;
sum += res;
if(sum >= q)
return true;
Insert(root, (int)p2[i].x);
}
return false;
}
ll CASE() {
scanf("%d%lld", &n, &q);
long double maxy = -INF, miny = INF;
for(int i = 1; i <= n; ++i) {
cin>>p[i].x>>p[i].y;
//scanf("%lf%lf", &p[i].x, &p[i].y);
if(p[i].y > maxy)
maxy = p[i].y;
if(p[i].y < miny)
miny = p[i].y;
}
ll L = -round(maxy - miny), R = round(maxy - miny), M;
while(1) {
M = (L + R) >> 1;
if(L == M) {
if(check(L))
return L;
if(check(R))
return R;
return INF;
}
if(check(M))
R = M;
else
L = M + 1;
}
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int T;
while(~scanf("%d", &T)) {
while(T--) {
ll res = CASE();
if(res >= INF)
puts("INF");
else
printf("%lld\n", res);
}
}
return 0;
}
事实上枚举斜率之后对式子变形:
\(\frac{y_1-y_2}{x_1-x_2}<=k\)
不妨设x1>x2
\(y_1-y_2<=k(x_1-x_2)\)
即:
\(y_1-kx_1<=y_2-kx_2\)
即满足 \(x1>x2\) 且 \(y_1-kx_1<=y_2-kx_2\) 的数对的个数。lzf大佬说是逆序对,太强了。
平衡树卡过去非常勉强:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
int n;
ll q;
int ch[MAXN][2];
ll val[MAXN];
int dat[MAXN], siz[MAXN], cnt[MAXN];
int tot, root;
inline void Init() {
tot = 0;
root = 0;
}
inline int NewNode(ll v) {
val[++tot] = v, dat[tot] = rand();
ch[tot][0] = ch[tot][1] = 0;
siz[tot] = 1, cnt[tot] = 1;
return tot;
}
inline void PushUp(int id) {
siz[id] = siz[ch[id][0]] + siz[ch[id][1]] + cnt[id];
}
inline void Rotate(int &id, int d) {
int temp = ch[id][d ^ 1];
ch[id][d ^ 1] = ch[temp][d];
ch[temp][d] = id;
id = temp;
PushUp(ch[id][d]), PushUp(id);
}
inline void Insert(int &id, ll v) {
if(!id)
id = NewNode(v);
else {
if(v == val[id])
++cnt[id];
else {
int d = v < val[id] ? 0 : 1;
Insert(ch[id][d], v);
if(dat[id] < dat[ch[id][d]])
Rotate(id, d ^ 1);
}
PushUp(id);
}
}
int GetRank(int id, ll v) {
if(!id)
return 0;
else {
if(v == val[id])
return siz[ch[id][1]] + cnt[id];
else if(v < val[id])
return siz[ch[id][1]] + cnt[id] + GetRank(ch[id][0], v);
else
return GetRank(ch[id][1], v);
}
}
const int INF = 1e9;
struct Point {
int x, y;
ll z;
Point() {}
Point(int x, int y): x(x), y(y) {}
bool operator<(const Point &p)const {
return (x != p.x) ? (x < p.x) : (y < p.y);
}
} p[MAXN];
bool check(int k) {
//printf("k=%d\n", k);
Init();
for(int i = 1; i <= n; ++i) {
p[i].z = 1ll * p[i].y - 1ll * k * p[i].x;
//printf("p[%d].z=%lld%c", i, p[i].z, " \n"[i == n]);
//printf("p[%d].x=%d%c", i, p[i].x, " \n"[i == n]);
}
ll sum = 0;
for(int i = 1, nxt; i <= n; i = nxt) {
for(nxt = i + 1; nxt <= n && p[nxt].x == p[i].x; ++nxt);
for(int j = i; j < nxt; ++j) {
//int gr = GetRank(root, p[j].z);
//printf("j=%d ,gr=%d\n", j, gr);
sum += GetRank(root, p[j].z);
//printf("sum=%lld\n", sum);
// if(sum >= q)
// return true;
}
for(int j = i; j < nxt; ++j)
Insert(root, p[j].z);
}
if(sum >= q)
return true;
return false;
}
int solve() {
scanf("%d%lld", &n, &q);
int maxy = -INF, miny = INF;
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &p[i].x, &p[i].y);
if(p[i].y > maxy)
maxy = p[i].y;
if(p[i].y < miny)
miny = p[i].y;
}
sort(p + 1, p + 1 + n);
/*for(int i = 1; i <= n; ++i) {
//p[i].z = 1ll * p[i].y - 1ll * k * p[i].x;
//printf("p[%d].z=%lld%c", i, p[i].z, " \n"[i == n]);
printf("p[%d].x=%d%c", i, p[i].x, " \n"[i == n]);
}*/
int L = -(maxy - miny), R = maxy - miny, M;
while(1) {
M = (L + R) >> 1;
if(L == M) {
if(check(L))
return L;
if(check(R))
return R;
return INF;
}
if(check(M))
R = M;
else
L = M + 1;
}
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int T;
while(~scanf("%d", &T)) {
while(T--) {
int res = solve();
if(res >= INF)
puts("INF");
else
printf("%d\n", res);
}
}
return 0;
}
求逆序对,用树状数组的话,是这样思考:把其中一维离散化,用树状数组来表示,按另一维顺序插入。
下面是把z离散化,按x从小到大插入,那么每次合法的就是前面的比它小的x2里面z2>=z1的,这样就先求出<=z1-1的个数,然后用已经插入的数的个数i-1减去,这里要注意相同的x的处理,600ms:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
int n;
ll q;
int bit[MAXN];
inline void Init() {
memset(bit, 0, sizeof(bit[0]) * (n + 1));
}
inline int Sum(int x) {
int res = 0;
while(x) {
res += bit[x];
x -= x & -x;
}
return res;
}
inline void Add(int x, int v) {
while(x <= n) {
bit[x] += v;
x += x & -x;
}
}
struct Point {
int x, y;
ll z;
Point() {}
Point(int x, int y): x(x), y(y) {}
bool operator<(const Point &p)const {
return x < p.x;
}
} p[MAXN];
ll pz[MAXN];
bool check(int k) {
Init();
for(int i = 1; i <= n; ++i)
pz[i] = p[i].z = 1ll * p[i].y - 1ll * k * p[i].x;
sort(pz + 1, pz + 1 + n);
int pzn = unique(pz + 1, pz + 1 + n) - (pz + 1);
for(int i = 1; i <= n; ++i)
p[i].z = lower_bound(pz + 1, pz + 1 + pzn, p[i].z) - pz;
ll sum = 0;
for(int i = 1, nxt; i <= n; i = nxt) {
for(nxt = i + 1; nxt <= n && p[nxt].x == p[i].x; ++nxt);
for(int j = i; j < nxt; ++j) {
sum += ((ll)i - 1) - Sum(p[j].z - 1);
if(sum >= q)
return true;
}
for(int j = i; j < nxt; ++j)
Add(p[j].z, 1);
}
return false;
}
const int INF = 1e9;
int solve() {
scanf("%d%lld", &n, &q);
int maxy = -INF, miny = INF;
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &p[i].x, &p[i].y);
if(p[i].y > maxy)
maxy = p[i].y;
if(p[i].y < miny)
miny = p[i].y;
}
sort(p + 1, p + 1 + n);
int L = -(maxy - miny), R = maxy - miny, M;
while(1) {
M = (L + R) >> 1;
if(L == M) {
if(check(L))
return L;
if(check(R))
return R;
return INF;
}
if(check(M))
R = M;
else
L = M + 1;
}
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int T;
while(~scanf("%d", &T)) {
while(T--) {
int res = solve();
if(res >= INF)
puts("INF");
else
printf("%d\n", res);
}
}
return 0;
}
离散化x的话,会更快,300ms,毕竟不用每次都对z进行一次unique然后lowerbound,的确少了一半的常数。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5;
int n;
ll q;
int bit[MAXN];
inline void Init() {
memset(bit, 0, sizeof(bit[0]) * (n + 1));
}
inline int Sum(int x) {
int res = 0;
while(x) {
res += bit[x];
x -= x & -x;
}
return res;
}
inline void Add(int x) {
while(x <= n) {
++bit[x];
x += x & -x;
}
}
struct Point {
int x, y, idx;
ll z;
Point() {}
Point(int x, int y): x(x), y(y) {}
bool operator<(const Point &p)const {
return z == p.z ? idx<p.idx: z > p.z;
}
} p[MAXN];
int px[MAXN], pxn;
bool check(int k) {
Init();
for(int i = 1; i <= n; ++i)
p[i].z = p[i].y - 1ll * k * p[i].x;
sort(p + 1, p + 1 + n);
ll sum = 0;
for(int i = 1; i <= n; ++i) {
sum += Sum(p[i].idx - 1);
if(sum >= q)
return true;
Add(p[i].idx);
}
return false;
}
const int INF = 1e9;
int solve() {
scanf("%d%lld", &n, &q);
int maxy = -INF, miny = INF;
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &p[i].x, &p[i].y);
px[i] = p[i].x;
if(p[i].y > maxy)
maxy = p[i].y;
if(p[i].y < miny)
miny = p[i].y;
}
sort(px + 1, px + 1 + n);
pxn = unique(px + 1, px + 1 + n) - (px + 1);
for(int i = 1; i <= n; ++i)
p[i].idx = lower_bound(px + 1, px + 1 + pxn, p[i].x) - px;
int L = miny - maxy, R = maxy - miny, M;
while(1) {
M = (L + R) >> 1;
if(L == M) {
if(check(L))
return L;
if(check(R))
return R;
return INF;
}
if(check(M))
R = M;
else
L = M + 1;
}
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int T;
while(~scanf("%d", &T)) {
while(T--) {
int res = solve();
if(res >= INF)
puts("INF");
else
printf("%d\n", res);
}
}
return 0;
}
SCUT - 484 - 平面上的点 - 数据结构的更多相关文章
- 平面上的地图搜索--Java学习笔记(四)
版权声明: 本文由Faye_Zuo发布于http://www.cnblogs.com/zuofeiyi/, 本文可以被全部的转载或者部分使用,但请注明出处. 这一个月以来,都在学习平面上的地图搜索,主 ...
- POJ C程序设计进阶 编程题#4:寻找平面上的极大点
编程题#4:寻找平面上的极大点 来源: POJ (Coursera声明:在POJ上完成的习题将不会计入Coursera的最后成绩.) 注意: 总时间限制: 1000ms 内存限制: 65536kB 描 ...
- COJN 0485 800503寻找平面上的极大点
800503寻找平面上的极大点 难度级别:C: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 在一个平面上,如果有两个点(x,y),(a,b) ...
- uva10245-The Closest Pair Problem(平面上的点分治)
解析:平面上的点分治,先递归得到左右子区间的最小值d,再处理改区间,肯定不会考虑哪些距离已经大于d的点对,对y坐标归并排序,然后从小到大开始枚举更新d,对于某个点,x轴方向只用考虑[x-d,x+d]( ...
- 平面上给定n条线段,找出一个点,使这个点到这n条线段的距离和最小。
题目:平面上给定n条线段,找出一个点,使这个点到这n条线段的距离和最小. 源码如下: #include <iostream> #include <string.h> #incl ...
- Problem E: 平面上的点和线——Point类、Line类 (V)
Description 在数学上,平面直角坐标系上的点用X轴和Y轴上的两个坐标值唯一确定,两点确定一条线段.现在我们封装一个“Point类”和“Line类”来实现平面上的点的操作. 根据“append ...
- Problem D: 平面上的点和线——Point类、Line类 (IV)
Description 在数学上,平面直角坐标系上的点用X轴和Y轴上的两个坐标值唯一确定,两点确定一条线段.现在我们封装一个“Point类”和“Line类”来实现平面上的点的操作. 根据“append ...
- Problem C: 平面上的点和线——Point类、Line类 (III)
Description 在数学上,平面直角坐标系上的点用X轴和Y轴上的两个坐标值唯一确定,两点确定一条线段.现在我们封装一个“Point类”和“Line类”来实现平面上的点的操作. 根据“append ...
- Problem B: 平面上的点和线——Point类、Line类 (II)
Description 在数学上,平面直角坐标系上的点用X轴和Y轴上的两个坐标值唯一确定,两点确定一条线段.现在我们封装一个“Point类”和“Line类”来实现平面上的点的操作. 根据“append ...
随机推荐
- 10. ClustrixDB 故障恢复管理
一.前端网络故障 如果节点无法在其前端以太网网络端口上进行通信,例如,由于意外的电缆拉拔.交换机配置错误或NIC故障,则不需要人工干预.集群采取以下行动: 没有将其他连接分配给失败的实例. 如果失败的 ...
- Codeforces Gym 100851 K King's Inspection ( 哈密顿回路 && 模拟 )
题目链接 题意 : 给出 N 个点(最多 1e6 )和 M 条边 (最多 N + 20 条 )要你输出一条从 1 开始回到 1 的哈密顿回路路径,不存在则输出 " There is no r ...
- 珍珠x
题目描述 有n颗形状和大小都一致的珍珠,它们的重量都不相同.n为整数,所有的珍珠从1到n编号.你的任务是发现哪颗珍珠的重量刚好处于正中间,即在所有珍珠的重量中,该珍珠的重量列(n+1)/2位.下面给出 ...
- window.location.href 兼容性问题 (ie 浏览器下设置失效)
window.location.href 兼容性问题 (ie 下设置失效) window.location.href = "../index.html" (ie 浏览器失效) wi ...
- [THUSC2017]杜老师:bitset+线性基
算法一(50pts) 分析 有一个很显然的暴力做法,对于区间内的每个数开个bitset,然后暴力分解质因数.如果对于一个数,它的一个质因子的指数是奇数,那么就把bitset的对应位设成\(1\).答案 ...
- python3.7--pycharm selenium自启360浏览器/360极速浏览器方法
写于:2019.01.02(实测日) 参考文档:https://blog.csdn.net/five3/article/details/50013159 一.下载360浏览器或360极速浏览器的Chr ...
- springboot上传文件大小限制的配置
springboot配置文件: application.properties #配置文件传输 spring.servlet.multipart.enabled =true spring.servlet ...
- C# 读取Excel中的数据到DataTable中
原文地址:http://www.open-open.com/code/view/1420029490093 public DataTable ExcelToDS(string Path) { stri ...
- 【Spark机器学习速成宝典】基础篇02RDD常见的操作(Python版)
目录 引例入门:textFile.collect.filter.first.persist.count 创建RDD的方式:parallelize.textFile 转化操作:map.filter.fl ...
- sourcetree pull push需要密码问题
我的是mac,以mac版本的sourcetree 为例 第一步 项目仓库右上角设置 第二步.点击远程仓库. 点击仓库路径点击编辑 第三步 url/路径修改 原本.https://gitee.com ...