题解 LGP8101【[USACO22JAN] Multiple Choice Test P】
problem
奶牛们正在参加一个选择题测试。在通常的测试中,对每个问题你的选项会被单独评分然后累加,而在此测试中,你的选项在累加之后再评分。
具体地说,你被给定二维平面上的 \(N\)(\(2 \le N \le 10^5\))组整数向量,其中每个向量用一个有序对 \((x,y)\) 表示。从每组中选择一个向量,使向量的总和尽可能远离原点。
输入保证向量的总数不超过 \(2 \times 10^5\)。每组至少包含 \(2\) 个向量,并且一组内所有向量各不相同。输入同时保证每个 \(x\) 和 \(y\) 坐标的绝对值不超过 \(\dfrac{10^9}{N}\)。
solution
这个题,观察到“向量的总和尽可能远离原点”这句话,如果只关心一组的向量,那么可能成为答案的向量是这个向量集的凸包,因为可以感性理解一下凸包里的点不会比凸包上的点答案更劣,凸包内的点加了其他向量也不如它到原点连线到凸包的交点,就是说只关心凸包上的点。
然后合并凸包可以用 Minkowski / 闵可夫斯基和。合并差分数组。
凸包求法:Graham
首先取一个 \(x\) 坐标最小,如果有多个取 \(y\) 坐标最小,的点,这个点是一定在凸包上的。以这个点为原点,对所有点做极角排序,然后做斜率优化(迫真)。就是维护栈,栈中的点连成的多边形始终保持斜率单调。
code
点击查看代码
观察到 minkowski 和的过程是合并差分,因此我们将所有凸包的差分拿出来一起排序。
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cassert>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
template<class T> struct vec{
T x,y;
vec operator+(const vec&rhs){return {x+rhs.x,y+rhs.y};}
vec operator-(const vec&rhs){return {x-rhs.x,y-rhs.y};}
vec operator*(const T&k){return {x*k,y*k};}
friend T cross(const vec&lhs,const vec&rhs){return lhs.x*rhs.y-lhs.y*rhs.x;}
friend T operator*(const vec&lhs,const vec&rhs){return lhs.x*rhs.x+lhs.y*rhs.y;}
friend bool quad(const vec&self){return self.x<=0&&self.y<0||self.x<0&&self.y>=0;}
bool operator<(const vec&rhs){return x!=rhs.x?x<rhs.x:y<rhs.y;}
friend LL dist(const vec&self){return 1ll*self.x*self.x+1ll*self.y*self.y;}
};
typedef vec<double> vec_t;
vector<vec_t> convexHull(vector<vec_t> pts){
if(pts.empty()) return {};
static vec_t stk[1<<19]; int top=0;
vec_t cen=*min_element(pts.begin(),pts.end());
sort(pts.begin(),pts.end(),[&](vec_t a,vec_t b){
if(quad(a-cen)!=quad(b-cen)) return quad(a-cen)<quad(b-cen);
if(cross(a-cen,b-cen)) return cross(a-cen,b-cen)>0;
return dist(a-cen)<dist(b-cen);
});
for(vec_t v:pts){
while(top>=2&&cross(stk[top-1]-stk[top],v-stk[top])>0) top--;
stk[++top]=v;
}
return vector<vec_t>(stk+1,stk+top+1);
}
int main(){
// #ifdef LOCAL
// freopen("input.in","r",stdin);
// #endif
int n;
scanf("%d",&n);
vector<vec_t> pts(n);
for(vec_t&v:pts) scanf("%lf%lf",&v.x,&v.y);
vector<vec_t> res=convexHull(pts);
double ans=0;
for(int i=0;i<res.size();i++) ans+=dist(res[i]-res[(i+1)%res.size()]);
printf("%.2lf\n",ans);
return 0;
}
当然也可以启发式合并。
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <cassert>
#include <utility>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
template<class T> using pqueue = priority_queue<T, vector<T>, greater<T>>;
template<class T> struct dot_t {
T x, y;
dot_t operator+(const dot_t& b) const { return {x + b.x, y + b.y}; }
dot_t operator-(const dot_t& b) const { return {x - b.x, y - b.y}; }
dot_t operator*(const T& k) const { return {x * k, y * k}; }
T operator*(const dot_t& b) const { return x * b.x + y * b.y; }
T friend cross(const dot_t& a, const dot_t& b) {
return a.x * b.y - a.y * b.x;
}
bool friend cmp(const dot_t& a, const dot_t& b) {
return cross(a, b) ? cross(a, b) > 0 : a.x < b.x;
}
bool friend operator<(const dot_t& a,const dot_t& b) {
return a.x != b.x ? a.x < b.x : a.y < b.y;
}
};
typedef dot_t<LL> dot;
vector<dot> convexHull(vector<dot> a) {
static dot stk[1 << 18];
dot cen = *min_element(a.begin(), a.end());
sort(a.begin(), a.end(),
[&](const dot& a,const dot& b) { return cmp(a - cen, b - cen); });
int top = 0;
for (dot v: a){
while (top >= 2 && cross(stk[top - 1] - stk[top], v - stk[top]) > 0)
top--;
stk[++top] = v;
}
return vector<dot>(stk + 1, stk + top + 1);
}
vector<dot> minkowski(const vector<dot>& a, const vector<dot>& b) {
vector<dot> c = {a[0] + b[0]};
static dot sa[1 << 18], sb[1 << 18];
int n = a.size(), m = b.size();
for (int i = 0; i < n; i++)
sa[i] = a[(i + 1) % n] - a[i];
for (int i = 0; i < m; i++)
sb[i] = b[(i + 1) % m] - b[i];
int i = 0, j = 0;
for (int k = 1; k < n + m; k++){
if (i < n && (j >= m || cmp(sa[i], sb[j])))
c.push_back(c.back() + sa[i++]);
else
c.push_back(c.back() + sb[j++]);
}
return c;
}
int n;
vector<dot> a[1 << 18];
pqueue<pair<int, int>> q;
int main() {
scanf("%d", &n);
for (int i = 1, k; i <= n; i++) {
scanf("%d", &k);
vector<dot> pts(k);
for (dot& v: pts)
scanf("%lld%lld", &v.x, &v.y);
a[i] = convexHull(pts);
q.push({a[i].size(), i});
}
for (int i = 1; i < n; i++) {
int u = q.top().second; q.pop();
int v = q.top().second; q.pop();
a[u] = minkowski(a[u], a[v]);
q.push({a[u].size(), u});
}
LL ans = -1e18;
for (dot v: a[q.top().second])
ans = max(ans, v * v);
printf("%lld\n",ans);
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-P8101.html