[LOJ#2329]「清华集训 2017」我的生命已如风中残烛

试题描述

九条可怜是一个贪玩的女孩子。

这天她在一堵墙钉了 \(n\) 个钉子,第 \(i\) 个钉子的坐标是 \((x_i,y_i)\)。接着她又在墙上钉上了 \(m\) 根绳子,绳子的一端是点 \(s_i(sx_i,sy_i)\),绳子经过点 \(t_i(tx_i,ty_i)\),同时绳子的长度是 \(L_i\)。其中 \(s_i\) 点是在墙上的,而另一个端点是可以移动的。初始情况下绳子是紧绷的一条直线段。

接着,对每一根绳子可怜都进行了一次游戏。在第 \(i\) 次游戏中,可怜会捏着第 \(i\) 根绳子的活动端点进行顺时针移动,移动过程中每时每刻绳子都是紧绷的。

不难发现绳子每时每刻都在以某一个位置为圆心作顺时针的圆周运动。初始情况下圆心是绳子的固定端点 \(s\),但是在运动的过程中圆心可能会不断发生变化,如下图所示:

图中左侧红点为钉子所在的点,右侧红点为绳子的固定端点,其他颜色的点为虚拟点,活动端点为紫点;绳子从紫点开始运动,在运行到蓝点时绳子绕上左侧红点的钉子,因此活动端点更换了圆心和半径,继续作顺时针的圆周运动。接着活动端点会运行到绿点,并且接下来活动端点会一直绕左侧钉子不停地做圆周运动。

不难发现绳子的运动是不会停止的,因此可怜在她感觉累了之后就会停下来。但是作为一个好奇心满满的女孩子,可怜决定对每一根绳子计算一下如果绳子无限的运行下去,那么它的圆心会切换多少次(包括初始的圆心)。不难发现这个数值一定是有限的。

这里对游戏过程进行一定程度的假设来简化问题:

  • 活动端点在运动的过程中距离每一个钉子的欧几里得距离始终大于等于 \(9 \times 10^{-4}\)。

  • 钉子的坐标两两不同但可能有多点共线

*钉子的体积以及绳子的体积可以忽略不计。在游戏的过程中每一段绳子之间不会互相影响。

  • 初始情况下绳子距离每一个钉子的最近欧几里得距离大于等于 \(9 \times 10^{-4}\)。

  • 每一根绳子初始粘着的端点不会影响绳子的运动,即绳子不会绕回到端点上

输入

从标准输入读入数据。

第一行输入一个整数 \(T\),表示数据组数。

每组数据第一行为两个整数 \(n,m\),表示钉子数和绳子数。

接下来 \(n\) 行,每行两个整数 \(x_i,y_i\) 表示钉子的坐标。

接下来 \(m\) 行,每行五个整数 \(sx_i,sy_i,tx_i,ty_i,L_i\) 表示一段绳子,含义如上所述。

输出

输出到标准输出。

对于每组数据的每组询问,输出一行一个整数表示运行的过程中圆心变化了多少次。

不同数据组数之间无需空行隔开。

输入示例

2
5 3
0 0
2 0
2 2
0 2
1 1
1 3 10 3 10
1 3 10 3 20
1 3 10 3 30
3 1
0 0
100 0
1000 0
1 3 10 3 1000000000

输出示例

6
11
16
1000001

数据规模及约定

对于前 \(10\%\) 的数据,\(n \leq 2\)。

对于前 \(20\%\) 的数据,\(n \leq 3\)。

对于前 \(30\%\) 的数据,\(n \leq 10\)。

对于前 \(60\%\) 的数据,\(n \leq 100\),\(m \leq 100\)。

对于 \(100\%\) 的数据,\(1 \leq n \leq 500\),\(1 \leq m \leq 500\),\(1\leq T \leq 10\)。

对于 \(100\%\) 的数据,\(0 \leq |x_i|,|y_i|,|sx_i|,|sy_i|,|tx_i|,|ty_i| \leq 10^4\),\(1 \leq L_i \leq 10^9\)。

题解

妈妈呀我终于卡过这题了!

等等我需要冷静一下才能写题解……

思路很直接,我们先考虑暴力,每次以一个点为原点,将所有点按照极角排序(\(O(n \mathrm{log}n)\)),然后再根据当前极角和剩余长度暴力扫一遍找到下一个点(\(O(n)\))。然后有可能出现循环,这个东西可以判一下如果一个点经过了两次并且这两次到达这个点时极角都一样,就表明找到了一个循环;那么可以算一下这个循环会被跑多少圈,跑完后增加多少步、剩下多少长度。这样做总共会有 \(n \mathrm{log}L\) 次转折点(考虑每经过一个循环就会使长度对某个数取模,即 \(L\) 最坏也会变成 \(\frac{L}{2}\),当 \(L\) 小于 \(1\) 时,由于是整点,就不可能再碰到其他点了),\(m\) 组询问,所以总复杂度 \(O(Tmn^2 \mathrm{log}L \mathrm{log}n)\)(对了还要乘个 \(T\)),纵然有 \(10\) 秒,也过不去啊……(像我这种 \(O(n^3)\) 都卡了一年才卡线过的……

瓶颈在于每次“找下一个点”的操作太费时了。考虑用预处理的方式优化。我们令 \(A(x, y, z)\) 表示轴心在点 \(x\),方向指向点 \(y\),当长度为 \(z\) 的时候恰好可以走到的下一个节点编号。那么预处理的时候我们先枚举点 \(x\),然后以 \(x\) 为原点极角排序,接下来一遍 \(O(n^2)\) 就可以搞定了。那么在询问时只需要二分一下极角,然后再二分一下长度,就可以在 \(O(\mathrm{log}n)\) 的时间内找到下一个点了。

最后贴上我又臭又长的代码。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <vector>
#include <cmath>
#include <ctime>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {if(Head == Tail) {int l = fread(buffer, 1, BufferSize, stdin);Tail = (Head = buffer) + l;}return *Head++;
}
int read() {int x = 0, f = 1; char c = getchar();while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }return x * f;
}#define maxn 501
#define LD long doubleconst LD eps = 1e-9;struct Vec {int x, y;Vec() {}Vec(int _, int __): x(_), y(__) {}LD operator - (const Vec& t) const {return sqrt((LD)(x - t.x) * (x - t.x) + (LD)(y - t.y) * (y - t.y));}
} ps[maxn], BaseP;int n, id[maxn], nsiz[maxn][maxn], nxt[maxn][maxn][maxn], angsiz[maxn];
double angle[maxn][maxn], pairAng[maxn][maxn], pairDis[maxn][maxn];int Base;
inline bool cmp(const int& A, const int& B) {Vec a = ps[A], b = ps[B];LD anga = pairAng[Base][A], angb = pairAng[Base][B],disa = a - ps[Base], disb = b - ps[Base];return fabs(anga - angb) > eps ? anga < angb : disa > disb;
}
inline bool cmp2(const int& A, const int& B) {Vec a = ps[A], b = ps[B];LD anga = atan2(a.x - BaseP.x, a.y - BaseP.y), angb = atan2(b.x - BaseP.x, b.y - BaseP.y),disa = a - BaseP, disb = b - BaseP;return fabs(anga - angb) > eps ? anga < angb : disa > disb;
}void init() {rep(i, 1, n) id[i] = i;rep(S, 1, n) rep(T, 1, n) {nsiz[S][T] = 0;if(S != T) pairAng[S][T] = atan2(ps[T].x - ps[S].x, ps[T].y - ps[S].y), pairDis[S][T] = ps[S] - ps[T];}rep(S, 1, n) {Base = S;sort(id + 1, id + n + 1, cmp);int cnt = 0;rep(T, 1, n) if(S != id[T] && (T == 1 || fabs(pairAng[S][id[T]] - pairAng[S][id[T-1]]) > eps)) {angle[S][++cnt] = pairAng[S][id[T]];rep(I, T + 1, n) if(id[I] != S && fabs(angle[S][cnt] - pairAng[S][id[I]]) > eps)if(!nsiz[S][cnt] || pairDis[S][nxt[S][cnt][nsiz[S][cnt]-1]] > pairDis[S][id[I]]) nxt[S][cnt][nsiz[S][cnt]++] = id[I];//*/rep(I, 1, n) if(id[I] != S) {if(!nsiz[S][cnt] || pairDis[S][nxt[S][cnt][nsiz[S][cnt]-1]] > pairDis[S][id[I]]) nxt[S][cnt][nsiz[S][cnt]++] = id[I];if(angle[S][cnt] < pairAng[S][id[I]]) break;}}angsiz[S] = cnt;}return ;
}LD atLen[maxn];
int atTag[maxn], hasPass[maxn];
int getAns(int p, LD ang, LD L) {memset(atTag, 0, sizeof(atTag));memset(hasPass, 0, sizeof(hasPass));memset(atLen, 0, sizeof(atLen));int tot = 1;while(1) {if(ang < angle[p][1]) ang = 1.0 / 0.0;int dir;int l = 1, r = angsiz[p] + 1;while(r - l > 1) {int mid = l + r >> 1;if(angle[p][mid] <= ang) l = mid; else r = mid;}dir = l;l = 0; r = nsiz[p][dir] - 1;while(l < r) {int mid = l + r >> 1;if(mid < nsiz[p][dir] - 1 && pairDis[nxt[p][dir][mid]][p] > L) l = mid + 1; else r = mid;}if(pairDis[nxt[p][dir][l]][p] > L) break;ang = pairAng[p][nxt[p][dir][l]];L -= pairDis[nxt[p][dir][l]][p]; tot++;p = nxt[p][dir][l];if(atTag[p]) {int cyc = (int)(L / (atLen[p] - L));tot += (tot - atTag[p]) * cyc;L -= (atLen[p] - L) * cyc;}if(hasPass[p]) atTag[p] = tot, atLen[p] = L;else hasPass[p] = 1;}return tot;
}void work() {n = read(); int q = read();rep(i, 1, n) {int x = read(), y = read();ps[i] = Vec(x, y);}init();while(q--) {int sx = read(), sy = read(), tx = read(), ty = read(), L = read();Vec s(sx, sy), t(tx, ty);LD ang = atan2(t.x - s.x, t.y - s.y);BaseP = Vec(sx, sy);sort(id + 1, id + n + 1, cmp2);bool has = 0;rep(I, 1, n) {Vec i = ps[id[I]];LD nang = atan2(i.x - s.x, i.y - s.y);if(nang >= ang && L > i - s){ printf("%d\n", getAns(id[I], nang, L - (i - s)) + 1); has = 1; break; }}if(!has) {ang = -1.0 / 0.0;rep(I, 1, n) {Vec i = ps[id[I]];LD nang = atan2(i.x - s.x, i.y - s.y);if(nang >= ang && L > i - s){ printf("%d\n", getAns(id[I], nang, L - (i - s)) + 1); has = 1; break; }}}if(!has) puts("1");}return ;
}int main() {int T = read();while(T--) work();return 0;
}

调得我已然成了风中残烛

转载于:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8058382.html

[LOJ#2329]「清华集训 2017」我的生命已如风中残烛相关推荐

  1. Loj #2324. 「清华集训 2017」小 Y 和二叉树

    Loj #2324. 「清华集训 2017」小 Y 和二叉树 小Y是一个心灵手巧的OIer,她有许多二叉树模型. 小Y的二叉树模型中,每个结点都具有一个编号,小Y把她最喜欢的一个二叉树模型挂在了墙上, ...

  2. uoj#344. 【清华集训2017】我的生命已如风中残烛(计算几何)

    题面 传送门 题解 orzxyx 首先我们发现,一个点如果被到达大于一次,那么这个点肯定在一个环上.所以在不考虑环的情况下每个点只会被到达一次,那么我们就可以直接暴力了 简单来说,我们对每个点\(i\ ...

  3. 「清华集训 2017」某位歌姬的故事

    题目链接 问题分析 吐槽一下这个预处理比DP还长的题-- 首先对限制从小到大排序,然后不难发现对于每一种大小限制都是独立的.离散后考虑\(F[i][j]\)表示以\(i\)结尾,上一个音高为限制大小的 ...

  4. UOJ#273. 【清华集训2016】你的生命已如风中残烛

    问题相当于有和为0的m个数 ai a i a_i,求 i i i有多少种排列使得任意一个前缀和都>=0 先给序列末尾加上一个-1,变成m+1个和为-1的数,要求前m个前缀和>=0 发现对于 ...

  5. 清华集训2017刷题记录

    2322. 「清华集训 2017」Hello world! 题意 一棵树每个点有点权,每次可以选择两个点\(s, t\),选择步长为\(k\),从\(s\)跳到\(t\)(不足\(k\)步直接到\(t ...

  6. [LOJ#522]「LibreOJ β Round #3」绯色 IOI(危机)

    [LOJ#522]「LibreOJ β Round #3」绯色 IOI(危机) 试题描述 IOI 的比赛开始了.Jsp 和 Rlc 坐在一个角落,这时他们听到了一个异样的声音 -- 接着他们发现自己收 ...

  7. 「清华名师讲坛」推荐

    在「清华名师讲坛」中我们可以看见两条「桥」,一条是跨越人文与科学的桥,一条是跨越中.西的桥. 这两条桥正好说明了清华大学的特质.清华无疑地是中国理工类大学的翘楚,是中国科学研究令人生敬的殿堂,但它也拥 ...

  8. [清华集训2016]你的生命已如风中残烛——组合数学

    题目链接: [清华集训2016]你的生命已如风中残烛 题目大意:共有$m+1$张牌,其中有$n$张特殊牌,每张特殊牌有一个权值$w_{i}$表示取到这张牌能获得$w_{i}$次再抽牌的机会,保证$\s ...

  9. loj #6226. 「网络流 24 题」骑士共存问题

    #6226. 「网络流 24 题」骑士共存问题 题目描述 在一个 n×n\text{n} \times \text{n}n×n 个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示.棋盘上某些 ...

最新文章

  1. python tkinter 布局_python tkinter学习——布局
  2. Nat. Mach. Intel. | IBM RXN: 深度学习在化学反应分类上大放异彩
  3. 【AI视野·今日CV 计算机视觉论文速览 第242期】Mon, 14 Feb 2022
  4. 让版面充满空间感的海报PSD分层模板,你一定要看看!
  5. js页面传值php页面,php实现跳转传值有什么方法,js页面跳转传值
  6. extjs combobox column布局为什么折叠在一起
  7. Java Formatter 阅读心得
  8. error RC2104: undefined keyword or key name问题
  9. Python入门--列表元素的判断及遍历,判断指定元素在列表中是否存在,列表元素的遍历,
  10. [Vani有约会]雨天的尾巴(树上差分+线段树合并)
  11. 火力发电行业三大知识图谱应用场景,助力火力发电厂清洁高效智慧化运营
  12. Linux不是Windows
  13. 什么是ACL和RBAC
  14. Python装逼神器,Python实现一键批量扣图
  15. 深度强化学习DDPG算法高性能Pytorch代码(改写自spinningup,低环境依赖,低阅读障碍)
  16. Siege 简单教程
  17. 数据库备份:Xtrabackup实现完全备份及增量备份
  18. 根据SQL必知必会学习SQL(MYSQL)
  19. 12亿次月访问流量网站服务器架构探秘
  20. 详细解析反爬手段以及处理方案

热门文章

  1. CAD调用移动命令(com接口)
  2. 读书百客:《疏影·苔枝缀玉》题解
  3. 南京廖华计算机二级考试答案,计算机二级MSOFFICE模拟考试题及答案题目
  4. 魔坊APP项目-21-种植园,宠物栏的功能实现、服务端提供显示宠物的api接口、客户端中展示宠物栏和宠物列表以及饱食度、宠物道具的使用
  5. IOS - 一键删除!拒绝苹果 iMessage 被垃圾信息骚扰
  6. php备份网站,php压缩备份整个网站
  7. PS 图像调整算法——自动对比度 (Auto Contrast)
  8. zeotero+oneDrive在两台设备pdf等附加文件无法同步的问题
  9. 李野默:《平凡的世界》 (最后三集)
  10. 介绍一下谷歌浏览器缓存在哪