(建议您从上方链接进入我的个人网站查看此 Blog,在 Luogu 中图片会被墙掉,部分 Markdown 也会失效)
给定平面上的
(n lines in total)
首先本题有一个比较显而易见的贪心,即最优的情况下每条射线至少穿过一条线段的端点,感性理解一下,如果不穿过端点那么移动到端点一定是不劣的。
我们可以考虑一个类似极角排序的思想,把每一个点当成一个向量的坐标表示,按照角度进行排序离散化并尝试寻找性质:
可以看下图:
排序离散化后,每个点旁边的数字即为其离散化后的坐标,于是我们发现,每一条线段都变成了一个区间:
同时注意,离散化后的每一个点都是至少一个区间的端点!
而我们从原点引出的每一条射线,也可以抽象成一个向量的坐标,或者换句话说,是这些区间中的一个点。
于是此时我们便可以发现问题变成了在给定区间中选择一些点,使这些点涉及的区间尽可能多,且每个区间中最多只有一个点。
对于这个问题应该是一个显然的 DP,我们考虑设
考虑转移,显然可以通过
显然选择第
于是我们的方程便为
通过差分预处理一下
然后注意观察到
时间复杂度是
Tips:对于具体的离散化实现,可以通过向量的外积,大于零则前者在后者的顺时针方向,反之亦然,可以考虑写个结构体重载小于号和等于号,或者在 sort
和 unique
和 lower_bound
的时候写个 lambda 函数也行,亦或自己纯手搓一个离散化也可以实现,具体可以看代码。
xxxxxxxxxx
981
2
3
4
5
6
7
8
9
10
11/******************************
12abbr
13segl => line segment
14lft => left
15******************************/
16
17using namespace std;
18using namespace __gnu_pbds;
19
20mt19937 rnd(random_device{}());
21int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
22bool rnddd(int x){return rndd(1, 100) <= x;}
23
24typedef unsigned int uint;
25typedef unsigned long long unll;
26typedef long long ll;
27typedef long double ld;
28
29template<typename T = int>
30inline T read(void);
31
32struct Point{
33 int x, y;
34 friend ll operator * (const Point &a, const Point &b){
35 return (ll)a.x * b.y - (ll)a.y * b.x;
36 }
37 friend bool operator < (const Point &a, const Point &b){
38 return a * b < 0;
39 }
40 friend bool operator == (const Point &a, const Point &b){
41 return a * b == 0;
42 }
43};
44int K, N;
45pair < Point, Point > segl[510000];
46pair < int, int > seg[510000];
47vector < Point > arr;
48int cnt[1100000];
49int lft[1100000];
50int dp[1100000];
51
52int main(){
53 K = read(), N = read();
54 for(int i = 1; i <= N; ++i){
55 int x1 = read(), y1 = read(), x2 = read(), y2 = read();
56 segl[i] = {Point{x1, y1}, Point{x2, y2}};
57 arr.push_back(Point{x1, y1}), arr.push_back(Point{x2, y2});
58 }
59 sort(arr.begin(), arr.end(), [](const Point &a, const Point &b)->bool{return a * b < 0;});
60 auto endpos = unique(arr.begin(), arr.end());
61 int siz = distance(arr.begin(), endpos);
62 for(int i = 1; i <= N; ++i){
63 seg[i].first = distance(arr.begin(), lower_bound(arr.begin(), endpos, segl[i].first) + 1);
64 seg[i].second = distance(arr.begin(), lower_bound(arr.begin(), endpos, segl[i].second) + 1);
65 if(seg[i].first > seg[i].second)swap(seg[i].first, seg[i].second);
66 }
67 for(int i = 1; i <= siz; ++i)lft[i] = siz;
68 for(int i = 1; i <= N; ++i){
69 ++cnt[seg[i].first], --cnt[seg[i].second + 1];
70 lft[seg[i].second] = min(lft[seg[i].second], seg[i].first);
71 }
72 for(int i = 1; i <= siz; ++i)cnt[i] += cnt[i - 1];
73 for(int i = siz - 1; i >= 1; --i)lft[i] = min(lft[i], lft[i + 1]);
74 for(int j = 1; j <= K; ++j){
75 for(int i = siz; i >= 1; --i)dp[i] = max(dp[i], dp[lft[i] - 1] + cnt[i]);
76 for(int i = 1; i <= siz; ++i)dp[i] = max(dp[i], dp[i - 1]);
77 }
78 printf("%d\n", dp[siz]);
79
80 fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
81 return 0;
82}
83
84template<typename T>
85inline T read(void){
86 T ret(0);
87 short flag(1);
88 char c = getchar();
89 while(c != '-' && !isdigit(c))c = getchar();
90 if(c == '-')flag = -1, c = getchar();
91 while(isdigit(c)){
92 ret *= 10;
93 ret += int(c - '0');
94 c = getchar();
95 }
96 ret *= flag;
97 return ret;
98}
update-2022_10_04 初稿