LG-P3562 [POI2013] LAS-Laser Solution

更好的阅读体验戳此进入

(建议您从上方链接进入我的个人网站查看此 Blog,在 Luogu 中图片会被墙掉,部分 Markdown 也会失效)

题面

给定平面上的 n 条线段(用点 (x1,y1),(x2,y2) 表示),你可以从原点引出最多 k 条射线,使其穿过尽可能多的线段,且每条线段最多被穿过 1 次,求最多可以穿过多少条线段。

1n5×105,1k100,1x1,y1,x2,y2105

输入格式

n,k

x1,y1,x2,y2

(n lines in total)

Solution

首先本题有一个比较显而易见的贪心,即最优的情况下每条射线至少穿过一条线段的端点,感性理解一下,如果不穿过端点那么移动到端点一定是不劣的。

我们可以考虑一个类似极角排序的思想,把每一个点当成一个向量的坐标表示,按照角度进行排序离散化并尝试寻找性质:

可以看下图:

Oops! The image is blocked! Please visit my own website to observe the image! 图片被墙了,请通过文章头的跳转链接访问!

排序离散化后,每个点旁边的数字即为其离散化后的坐标,于是我们发现,每一条线段都变成了一个区间:

[1,1],[7,8],[3,6],[4,5],[5,5],[2,9]

同时注意,离散化后的每一个点都是至少一个区间的端点

而我们从原点引出的每一条射线,也可以抽象成一个向量的坐标,或者换句话说,是这些区间中的一个点。

于是此时我们便可以发现问题变成了在给定区间中选择一些点,使这些点涉及的区间尽可能多,且每个区间中最多只有一个点。

对于这个问题应该是一个显然的 DP,我们考虑设 dp(i,j) 表示在前 i 个点(等同于区间端点)中选择 j 个点最多可以涉及多少个区间。

考虑转移,显然可以通过 dp(i1,j) 转移,同时我们也可以考虑如果选择第 i 个点,那么应该从什么区间转移而来?

显然选择第 i 个点后,其所涉及的所有区间都不能有其它的点,所以我们应该记录 i 所涉及的所有的区间中最左的端点,记其为 lft(i),同时记点 i 涉及的区间的数量为 cnt(i),那么也就是从 dp(lft(i)1,j1)+cnt(i) 转移而来。

于是我们的方程便为 dp(i,j)=max(dp(i1,j),dp(lft(i)1,j1)+cnt(i))

通过差分预处理一下 lftcnt 即可。

然后注意观察到 i 的值域是 5×105j 的值域是 102,所以空间占用为 5×105×102×410242>128,空间会炸,发现 j 只会从 jj1 转移而来,所以可以以类似写背包时候的思想把 j 的维度去掉,将不同版本考虑好即可。

时间复杂度是 O(n(logn+k)) 的,显然可以通过。

Tips:对于具体的离散化实现,可以通过向量的外积,大于零则前者在后者的顺时针方向,反之亦然,可以考虑写个结构体重载小于号和等于号,或者在 sortuniquelower_bound 的时候写个 lambda 函数也行,亦或自己纯手搓一个离散化也可以实现,具体可以看代码。

Code

UPD

update-2022_10_04 初稿