@codeforces - 607E@ Cross Sum
@description@
给定 n 条直线,构造可重点集合 I 为 n 条直线两两的交点集合。
再给定一点 (p, q)。求该点到 I 中的点的距离中前 m 小的距离之和。
@solution@
求前 m 小,而 m 又这么大,不难想到二分答案。
二分答案 r,可以得到一个以 (p, q) 为圆心、以 r 为半径的圆。
我们想要求圆内直线的交点个数 cnt 并判断是否有 cnt >= m。
注意到此时直线在圆中呈现弦的形式。而圆中弦的相交,可以通过将端点转成极角序,然后判断两个区间严格相交(没有包含的情况)来实现。这个直接树状数组随便做。
然而上面只能解决严格在圆内的交点,恰好在圆上的交点,需要判断一个端点连接了多少条不同的直线来求解。
最后我们得到第 m 大的距离在 [l, l + eps] 之间,且这个区间之间只含这一种距离。
怎么求前 m 大之和呢?我们可以一一找出以 l 为半径的这个圆中的直线交点。先找出严格在圆内的:直接作一个二维偏序即可。
此时在圆上的交点个数可能非常多,所以我们不能取 (l + eps) 为半径,不然就会把这些交点算进严格在圆内的。
圆上的交点个数 = (m - 严格在圆内的交点个数)。
@accepted code@
#include <cmath>
#include <cstdio>
#include <vector>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
#define double long double
const int MAXN = 100000;
const double EPS = 1E-16;
const double PI = acos(-1);
int dcmp(double x) {
return (fabs(x) <= EPS ? 0 : (x < 0 ? -1 : 1));
}
int n, m; double K[MAXN + 5], B[MAXN + 5];
double t[MAXN + 5]; int cnt;
bool cmp(double a, double b) {return dcmp(a - b) < 0;}
bool cmp2(double a, double b) {return dcmp(a - b) == 0;}
int tree[MAXN + 5], le[MAXN + 5], ri[MAXN + 5];
int lowbit(int x) {
return x & -x;
}
int sum(int x) {
int ret = 0;
for(int i=x;i;i-=lowbit(i))
ret += tree[i];
return ret;
}
void update(int x, int k) {
for(int i=x;i<=cnt;i+=lowbit(i))
tree[i] += k;
}
int tmp[MAXN + 5];
vector<int>v1[MAXN + 5], v2[MAXN + 5];
bool check(double r) {
cnt = 0;
for(int i=1;i<=n;i++) {
double del = K[i]*K[i]*r*r + r*r - B[i]*B[i];
if( dcmp(del) >= 0 ) {
double frac = K[i]*K[i] + 1; del = sqrt(del);
double x1 = -(del + B[i]*K[i])/frac, y1 = -(K[i]*del - B[i])/frac;
t[++cnt] = atan2(y1, x1);
double x2 = -(-del + B[i]*K[i])/frac, y2 = (K[i]*del + B[i])/frac;
t[++cnt] = atan2(y2, x2);
}
}
sort(t + 1, t + cnt + 1, cmp);
cnt = unique(t + 1, t + cnt + 1, cmp2) - t - 1;
for(int i=1;i<=cnt;i++)
tree[i] = tmp[i] = 0, v1[i].clear(), v2[i].clear();
for(int i=1;i<=n;i++) {
double del = K[i]*K[i]*r*r + r*r - B[i]*B[i];
if( dcmp(del) >= 0 ) {
double frac = K[i]*K[i] + 1; del = sqrt(del);
double x1 = -(del + B[i]*K[i])/frac, y1 = -(K[i]*del - B[i])/frac;
int p = lower_bound(t + 1, t + cnt + 1, atan2(y1, x1), cmp) - t;
double x2 = -(-del + B[i]*K[i])/frac, y2 = (K[i]*del + B[i])/frac;
int q = lower_bound(t + 1, t + cnt + 1, atan2(y2, x2), cmp) - t;
le[i] = min(p, q), ri[i] = max(p, q);
v1[le[i]].push_back(i), v2[ri[i]].push_back(i);
tmp[le[i]]++;
if( le[i] != ri[i] ) tmp[ri[i]]++;
}
}
int tot = 0;
for(int i=1;i<=cnt;i++) {
for(int j=0;j<v1[i].size();j++) update(i, 1);
for(int j=0;j<v2[i].size();j++)
update(le[v2[i][j]], -1), tot += sum(i-1) - sum(le[v2[i][j]]);
tot += 1LL*tmp[i]*(tmp[i] - 1)/2;
}
return tot >= m;
}
struct node{
int le, ri, id;
friend bool operator < (node a, node b) {return a.ri < b.ri;}
}b[MAXN + 5], c[MAXN + 5];
double ans; int nw;
double intersect(int i, int j) {
double x = (B[j] - B[i]) / (K[i] - K[j]);
double y = K[i] * x + B[i];
return sqrt(x*x + y*y);
}
void solve(int le, int ri) {
if( le == ri ) return ;
int m = (le + ri) >> 1;
solve(le, m), solve(m + 1, ri);
int p = le, q = m + 1, t = le;
while( p <= m && q <= ri ) {
if( b[p].le < b[q].le ) {
for(int i=q;i<=ri;i++) {
if( b[i].le >= b[p].ri ) break;
if( b[p].ri < b[i].ri ) nw++, ans += intersect(b[p].id, b[i].id);
}
c[t++] = b[p++];
}
else c[t++] = b[q++];
}
while( p <= m ) c[t++] = b[p++];
while( q <= ri ) c[t++] = b[q++];
for(int i=le;i<=ri;i++) b[i] = c[i];
}
double get(double r) {
cnt = 0;
for(int i=1;i<=n;i++) {
double del = K[i]*K[i]*r*r + r*r - B[i]*B[i];
if( dcmp(del) >= 0 ) {
double frac = K[i]*K[i] + 1; del = sqrt(del);
double x1 = -(del + B[i]*K[i])/frac, y1 = -(K[i]*del - B[i])/frac;
t[++cnt] = atan2(y1, x1);
double x2 = -(-del + B[i]*K[i])/frac, y2 = (K[i]*del + B[i])/frac;
t[++cnt] = atan2(y2, x2);
}
}
sort(t + 1, t + cnt + 1, cmp);
cnt = unique(t + 1, t + cnt + 1, cmp2) - t - 1;
int tot = 0;
for(int i=1;i<=n;i++) {
double del = K[i]*K[i]*r*r + r*r - B[i]*B[i];
if( dcmp(del) >= 0 ) {
double frac = K[i]*K[i] + 1; del = sqrt(del);
double x1 = -(del + B[i]*K[i])/frac, y1 = -(K[i]*del - B[i])/frac;
int p = lower_bound(t + 1, t + cnt + 1, atan2(y1, x1), cmp) - t;
double x2 = -(-del + B[i]*K[i])/frac, y2 = (K[i]*del + B[i])/frac;
int q = lower_bound(t + 1, t + cnt + 1, atan2(y2, x2), cmp) - t;
tot++, b[tot].le = min(p, q), b[tot].ri = max(p, q), b[tot].id = i;
}
}
sort(b + 1, b + tot + 1), solve(1, tot);
return ans + 1.0*(m - nw)*r;
}
int read() {
int x = 0, f = 1; char ch = getchar();
while( (ch != '-') && (ch > '9' || ch < '0') ) ch = getchar();
if( ch == '-' ) f = -1, ch = getchar();
while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
return x * f;
}
int main() {
double x, y;
n = read(), x = 1.0*read()/1000, y = 1.0*read()/1000, m = read();
for(int i=1;i<=n;i++) {
K[i] = 1.0*read()/1000, B[i] = 1.0*read()/1000;
B[i] = K[i]*x + B[i] - y;
}
/*
for(int i=1;i<=n;i++) {
for(int j=i+1;j<=n;j++)
printf("%d %d : ", i, j), cout << dist(intersect(l[i], l[j]), pnt) << endl;
cout << i << " " << (pnt - a[i]) * l[i].ab << endl;
}
*/
double le = 0, ri = 2E9 + 5000;
for(int i=0;i<=50;i++) {
double mid = (le + ri) / 2;
if( check(mid) ) ri = mid;
else le = mid;
}
cout << fixed << setprecision(9) << get(le) << endl;
}
@details@
本来思路很简单,结果它先是卡我精度然后又卡我常。。。
写几点做题时遇到的问题:
(1)可以先把直线平移,并把 (p, q) 平移成坐标系原点
(2)不要觉得向量表达就是万能的,这道题恰巧向量表达精度损失非常大。。。反而用直线与圆的方程联立来得更直接。
(3)二分次数不要太多小心 TLE。cin 是真的慢,能换掉就换。
@codeforces - 607E@ Cross Sum的更多相关文章
- Codeforces 396B On Sum of Fractions 数论
题目链接:Codeforces 396B On Sum of Fractions 题解来自:http://blog.csdn.net/keshuai19940722/article/details/2 ...
- codeforces 963A Alternating Sum
codeforces 963A Alternating Sum 题解 计算前 \(k\) 项的和,每 \(k\) 项的和是一个长度为 \((n+1)/k\) ,公比为 \((a^{-1}b)^k\) ...
- codeforces 1217E E. Sum Queries? (线段树
codeforces 1217E E. Sum Queries? (线段树 传送门:https://codeforces.com/contest/1217/problem/E 题意: n个数,m次询问 ...
- Codeforces 577B Modulo Sum
http://codeforces.com/problemset/problem/577/B 题意:有n个数,求有无一个子序列满足和是m的倍数 思路:用模下的背包做,发现n是十的六次方级别,但是有个神 ...
- Codeforces 85 D. Sum of Medians
题目链接:http://codeforces.com/contest/85/problem/D 做法果然男默女泪啊..... 大概就是直接开了一个$vector$每次插入删除都用自带的$insert$ ...
- Codeforces 797B - Odd sum
B. Odd sum 题目链接:http://codeforces.com/problemset/problem/797/B time limit per test 1 second memory l ...
- Codeforces 963A Alternating Sum(等比数列求和+逆元+快速幂)
题目链接:http://codeforces.com/problemset/problem/963/A 题目大意:就是给了你n,a,b和一段长度为k的只有'+'和‘-’字符串,保证n+1被k整除,让你 ...
- Educational Codeforces Round 37-F.SUM and REPLACE题解
一.题目 二.题目链接 http://codeforces.com/contest/920/problem/F 三.题意 给定$N$个范围在$[1, 1e6)$的数字和$M$个操作.操作有两种类型: ...
- codeforces 85D D. Sum of Medians Vector的妙用
D. Sum of Medians Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/problemset/prob ...
随机推荐
- redis学习——day01_redis简介与安装
一.Redis 简介 1.1 Redis是什么 REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统.Redi ...
- mysql单记录也能造成的死锁
最近在开发的时候,在mysql Innodb 引擎下,一条记录记录也能引起锁的事件. 场景描述 在项目压测的是,突然发现有类似以下的异常发生: com.mysql.jdbc.exceptions.jd ...
- Android数据传递
直接用一个例子说明,简单粗暴: 数据传递会用到此界面标注id值的三个控件 Activity_zc.xm l 当点击“注册”按钮,会显示注册信息 Activity._show.xml 下面展示zcAct ...
- 理解session及微信小程序使用session
session介绍 由于Http是无状态的协议,所以服务端需要记录用户的状态时,就需要某种机制来识别具体的用户,实现这个机制的方式就是session. 典型的场景比如购物车,当你点击下单按钮时,由于H ...
- 我的Android知识结构图——20200507停止更新,后续通过标签或分类继续完善结构图
*持续更新中.调整中(带链接的是已经总结发布的,未带链接是待发布的) *个别知识点在多个分类中都是比较重要部分,为了分类完整性 可能多出都列出了 *每一篇都是认真总结并写出来的,若哪里有问题欢迎指正 ...
- Oracle 利用PLSQL一分钟将表结构(PROJ),从A库移植到B库,一分钟将A库中表数据移植到B库中!!!
导读(苦恼) 做多个项目的时候,可能会有这样的需求,需要把A项目中的某些功能移植到B项目上:移植途中,牵扯到顺便把表也要一块移植过去,若表字段较少,那还好,可能耗费10分钟就搞完了,万一碰上几十个字段 ...
- [JavaWeb基础] 016.Struts2 国际化配置
如果一个软件想要让其受众是全球或者是几个国家的人,那么这个软件就需要支持多种语言,那么我们就需要软件的国际化去对一些文字信息进行国际化处理.web也一样,当外国人打开我们的网站,要是看到满屏幕的中文, ...
- 新手福利!Blazor 从入门到砖家系列教程(你真的可以成为砖家)
注意 作为 Blazor 新手的你(不是新手就当没看到这句话),请一定走完本教程再去上手其他组件库 标题跟你开个玩笑,你真的可以通过这个系列教程成为专家! 深入浅出,我翻译过程中都学到了很多. 我们不 ...
- Ratel源码-C/S事件梳理
一.Ratel介绍 Ratel 是一个可以在命令行中玩斗地主的项目,可以使用小巧的jar包在拥有JVM环境的终端中进行游戏,同时支持人人对战和人机对战两种模式,丰富你的空闲时间! 二.玩法Demo 三 ...
- python调用大漠插件教程04鼠键事件及基本项目思维
from win32com.client import Dispatch import os from random import uniform from time import sleep cla ...