题解 - 凸包切线 + 环形区间覆盖 2018-2019 ACM-ICPC, Asia Xuzhou Regional Contest M Rikka with Illuminations
题意:
给你一个凸n边形,和m个灯(点)。问至少打开哪些灯才能照亮整个凸包?
保证灯在凸包外且不会和凸多边形的某边三点共线。 n , m ≤ 1000 n,m\le1000 n,m≤1000
银牌几何题。
分析:
首先,画一下某个灯“照亮”的范围,发现被照亮的区域等价于该灯与凸包的两条切线所夹的那部分区域。
于是对于每个点找到其关于凸包的两个切点。然后问题就变为了一个给你一个环和m个区间 ( l , r ) (l,r) (l,r),至少选哪几个区间可以覆盖整个圆。
挺直观的,后半部分代码能力有待提高。
实现:
-
O ( n m ) O(nm) O(nm)求某点 p [ i ] p[i] p[i]关于凸包的切点:
我们只要逆时针枚举凸包上的点 c h [ j ] ch[j] ch[j],计算
now = (ch[j%n] - p[i]) | (ch[(j + 1)%n] - p[i]);
。如果相邻的两个now符号不同,则说明那个点是切点。 注意只算出两个切点是不够的,还要知道它代表环上哪边的区域。所以我们要确定起点和终点(不妨设逆时针绕)。画一下就能得出:
if (last >0 && now< 0) range[i].first = j%n; if (last<0 && now>0) range[i].second = j%n;
清华大学 24726 rsa:“可以用二分法优化到mlogn,然而很难写”
-
O ( n 2 ) O(n^2) O(n2)环形区间覆盖:
按套路处理环:拉直再翻倍。
对应到每个区间 ( l , r ) (l,r) (l,r) 就是
- 如果 l > r , r + = n l>r,r+=n l>r,r+=n。
- 对每个区间再增加一个 ( l + n , r + n ) (l+n,r+n) (l+n,r+n)的区间
先枚举每个区间作为起点,然后贪心地做。维护当前区间的最右端点nowr,每次将l小于nowr的区间的右端点取个max,用来更新nowr。最后如果nowr>起点区间的l+n,用它更新答案。
注意一个细节,若某次右端点取max的值小于nowr,则直接跳出循环。否则死循环了orz
rep(i, 1, m) { int nowr = trange[i].r; int p = i + 1; int f = 1; vector<int >tmp; tmp.push_back(trange[i].rk); auto mx = Range(0, -1, 0); while (nowr < trange[i].l + n) { while (p<=2*m&&trange[p].l <= nowr)mx = max(mx, trange[p++]); if(p >= 2 * m||mx.r<=nowr) { f = 0; break; } nowr = mx.r; tmp.push_back(mx.rk); } if (f == 0)continue; if (tmp.size() < ans.size())ans = tmp; }
代码
#include <iomanip> #include <cstring> #include <cstdlib> #include <cctype> #include <cstdio> #include <string> #include <stack> #include <cmath> #include <ctime> #include <list> #include <set> #include <map> #include <queue> #include <vector> #include <sstream> #include <memory> #include <iostream> #include <algorithm> using namespace std; #define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++) #define per(i,j,k) for(int i = (int)j;i >= (int)k;i --) #define debug(x) cerr<<#x<<" = "<<(x)<<endl #define mmm(a,b) memset(a,b,sizeof(a)) #define pb push_back typedef double db; typedef long long ll; const int MAXN = (int)1e5 + 5; const int INF = (int)0x3f3f3f3f; const int maxn = 1e4 + 5; int n, m; const double eps = 1e-7; struct V { ll x, y; V() {} void sc() { scanf("%lld%lld", &x, &y); } V(ll a, ll b) : x(a), y(b) { } V operator+(V o) { return V(x + o.x, y + o.y); } V operator-(V o) { return V(x - o.x, y - o.y); } ll operator|(V o) { return x * o.y - o.x * y; } void pr() { printf("%lld %lld\n", x, y); } } p[maxn]; vector<V> ch; int out(int x) { return x ? x - 1 : ch.size() - 1; } int in(int x) { return x + 1 == (int)ch.size() ? 0 : x + 1; } struct Range{ int l; int r; int rk; Range(int l=0, int r=0, int rk=0) :l(l), r(r), rk(rk) {} bool operator < (const Range & b)const { return r < b.r; } }trange[maxn*2]; pair<int, int> range[maxn]; vector<int> ans; bool cmp(Range a, Range b) { return a.l < b.l; } int main() { int t; cin >> t; while (t--) { ch.clear(); cin >> n >> m; rep(i, 1, n) { V tmp; cin >> tmp.x >> tmp.y; ch.push_back(tmp); } rep(i, 1, m) p[i].sc(); rep(i, 1, m) { int j = 0; db last = (ch[j] - p[i]) | (ch[j + 1] - p[i]); rep(j, 1, n) { db now = (ch[j%n] - p[i]) | (ch[(j + 1)%n] - p[i]); //if (sgn(now) == 0)continue; if (last >0 && now< 0) range[i].first = j%n; if (last<0 && now>0) range[i].second = j%n; last = now; } } rep(i, 1, m) { //cout << range[i].first << ' ' << range[i].second << endl; //pt[range[i]] = i; trange[i].l = range[i].first; trange[i].r = range[i].second; if (trange[i].r < trange[i].l)trange[i].r += n; trange[i].rk = i; } rep(i, 1, m) { trange[m + i] = Range(trange[i].l + n, trange[i].r + n, trange[i].rk); } sort(trange + 1, trange + 1 + 2*m,cmp); ans.clear(); ans.resize(m+1); rep(i, 1, m) { int nowr = trange[i].r; int p = i + 1; int f = 1; vector<int >tmp; tmp.push_back(trange[i].rk); auto mx = Range(0, -1, 0); while (nowr < trange[i].l + n) { while (p<=2*m&&trange[p].l <= nowr)mx = max(mx, trange[p++]); if(p >= 2 * m||mx.r<=nowr) { f = 0; break; } nowr = mx.r; tmp.push_back(mx.rk); } if (f == 0)continue; if (tmp.size() < ans.size())ans = tmp; } if (ans.size() == m + 1)cout << -1 << endl; else { cout << ans.size() << endl; rep(j, 0, ans.size() - 1)cout << ans[j] << (j == ans.size() - 1 ? '\n' : ' '); //for (auto t : ans)cout << t << ' '; //cout << endl; } } cin >> t; } /* */