极角排序
1. gym102465F
题意:给定$n$个有权值的平面点,求选两点连线,使得直线两边权值和差最小
思路就是枚举每个点极角排序,滑动区间维护长π的区间的权值和
#include <bits/stdc++.h> using namespace std; const int N = 4e3+10; const double pi = acos(-1); struct point { int x,y,z; } a[N]; pair<double,int> b[N*2]; int main() { int n; scanf("%d", &n); int64_t sum = 0; for (int i=1; i<=n; ++i) { scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z); sum += a[i].z; } int64_t ans = sum; for (int i=1; i<=n; ++i) { for (int j=1; j<=n; ++j) if (i!=j) { b[j-(i<j)] = {atan2(a[j].y-a[i].y,a[j].x-a[i].x),a[j].z}; } sort(b+1,b+n); for (int j=1; j<n; ++j) b[j+n-1] = {b[j].first+2*pi,b[j].second}; int now = 1; int64_t w = 0; for (int j=1; j<n; ++j) { while (now<=2*n-2&&b[now].first-b[j].first<pi) w += b[now++].second; ans = min(ans, abs((w-b[j].second)-(sum-a[i].z-w))); w -= b[j].second; } } printf("%lld\n", ans); }
#include <bits/stdc++.h> using namespace std; const int N = 4e3+10; const double pi = acos(-1); struct point { int x,y,z,tp; bool operator < (const point & b) const { if (tp!=b.tp) return tp<b.tp; return y*(int64_t)b.x<x*(int64_t)b.y; } } a[N],b[N*2]; int get(int x, int y) { if (x>0&&y>=0) return 1; if (x<=0&&y>0) return 2; if (x<0&&y<=0) return 3; return 4; } int main() { int n; scanf("%d", &n); int64_t sum = 0; for (int i=1; i<=n; ++i) { scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z); sum += a[i].z; } int64_t ans = sum; for (int i=1; i<=n; ++i) { int tot = 0; for (int j=1; j<=n; ++j) if (i!=j) { ++tot; b[tot] = {a[j].x-a[i].x,a[j].y-a[i].y,a[j].z}; b[tot].tp = get(b[tot].x,b[tot].y); } sort(b+1,b+tot+1); for (int j=1; j<n; ++j) ++tot, b[tot] = b[j], b[tot].tp += 4; int now = 1; int64_t w = 0; for (int j=1; j<n; ++j) { point u{-b[j].x,-b[j].y,b[j].z,b[j].tp+2}; while (now<=tot&&b[now]<u) w += b[now++].z; ans = min(ans, abs((w-b[j].z)-(sum-a[i].z-w))); w -= b[j].z; } } printf("%lld\n", ans); }
2. gym102361A
题意:给定$n$个点$P_i$,$q$个询问,给定点$A_i$,求多少二元组$(u,v)$,满足$A_i,P_u,P_v$为直角三角形
按每个询问点极角排序,可以求出询问点为直角顶点的答案。然后离线按每个给定点极角排序,求出每个询问点为锐角顶点的答案
#include <bits/stdc++.h> using namespace std; const int N = 4e3+10; struct point { int x,y,tp; bool operator < (const point & b) const { if (tp!=b.tp) return tp<b.tp; return y*(int64_t)b.x<x*(int64_t)b.y; } bool operator <= (const point &b) const { return !(b<*this); } } a[N],b[N],f[N*2]; pair<point,int> g[N]; int ans[N]; int get(int x, int y) { if (x>0&&y>=0) return 1; if (x<=0&&y>0) return 2; if (x<0&&y<=0) return 3; return 4; } int main() { int n, q; scanf("%d%d", &n, &q); for (int i=1; i<=n; ++i) scanf("%d%d", &a[i].x, &a[i].y); for (int i=1; i<=q; ++i) { scanf("%d%d", &b[i].x, &b[i].y); for (int j=1; j<=n; ++j) { f[j] = {a[j].x-b[i].x,a[j].y-b[i].y}; f[j].tp = get(f[j].x,f[j].y); } sort(f+1,f+1+n); for (int j=1; j<=n; ++j) f[j+n] = f[j], f[j+n].tp += 4; int lx = 1, rx = 1; for (int j=1; j<=n; ++j) { point u{-f[j].y,f[j].x,f[j].tp+1}; while (lx<=2*n&&f[lx]<u) ++lx; while (rx<=2*n&&f[rx]<=u) ++rx; ans[i] += rx-lx; } } for (int i=1; i<=n; ++i) { int tot = 0; for (int j=1; j<=n; ++j) if (i!=j) { f[++tot] = {a[j].x-a[i].x,a[j].y-a[i].y}; f[tot].tp = get(f[tot].x,f[tot].y); } sort(f+1,f+1+tot); for (int j=1; j<n; ++j) ++tot, f[tot] = f[j], f[tot].tp += 4; for (int j=1; j<=q; ++j) { g[j].first = {b[j].x-a[i].x,b[j].y-a[i].y}; g[j].first.tp = get(g[j].first.x,g[j].first.y); g[j].second = j; } sort(g+1,g+1+q); int lx = 1, rx = 1; for (int j=1; j<=q; ++j) { point v{-g[j].first.y,g[j].first.x,g[j].first.tp+1}; while (lx<=tot&&f[lx]<v) ++lx; while (rx<=tot&&f[rx]<=v) ++rx; ans[g[j].second] += rx-lx; } lx = rx = tot; for (int j=q; j; --j) { point v{g[j].first.y,-g[j].first.x,g[j].first.tp-1+4}; while (lx>=1&&v<f[lx]) --lx; while (rx>=1&&v<=f[rx]) --rx; ans[g[j].second] += lx-rx; } } for (int i=1; i<=q; ++i) printf("%d\n", ans[i]); }
因为只用求直角,可以不极角排序,直接把向量reduce后判断
#include <bits/stdc++.h> using namespace std; const int N = 2e3+10; int n, q; struct point { int x, y; } a[N], b[N]; pair<int,int> f[N]; int ans[N]; int gcd(int x, int y) {return y?gcd(y,x%y):x;} pair<int,int> reduce(int x, int y) { int g = gcd(abs(x),abs(y)); if (g) x /= g, y /= g; return pair<int,int>(x,y); } int main() { scanf("%d%d", &n, &q); for (int i=1; i<=n; ++i) scanf("%d%d", &a[i].x, &a[i].y); for (int i=1; i<=q; ++i) { scanf("%d%d", &b[i].x, &b[i].y); for (int j=1; j<=n; ++j) f[j] = reduce(a[j].y-b[i].y,a[j].x-b[i].x); sort(f+1,f+1+n); for (int j=1; j<=n; ++j) { pair<int,int> u = reduce(-f[j].second,f[j].first); ans[i] += upper_bound(f+1,f+1+n,u)-lower_bound(f+1,f+1+n,u); } } for (int i=1; i<=n; ++i) { for (int j=1; j<=n; ++j) if (i!=j) f[j-(i<j)] = reduce(a[j].y-a[i].y,a[j].x-a[i].x); sort(f+1,f+n); for (int j=1; j<=q; ++j) { pair<int,int> u = reduce(-(b[j].x-a[i].x),b[j].y-a[i].y); ans[j] += upper_bound(f+1,f+n,u)-lower_bound(f+1,f+n,u); u.first = -u.first, u.second = -u.second; ans[j] += upper_bound(f+1,f+n,u)-lower_bound(f+1,f+n,u); } } for (int i=1; i<=q; ++i) printf("%d\n", ans[i]); }
3. gym102428D
题意:$n$个带权平面点,求判断是否存在一条直线,使得直线平移依次经过所有点时,点权是递增的
把所有权值小的点向大的连一条向量,然后极角排序,如果存在两个相邻向量夹角不小于$\pi$,那么有解,否则无解
#include <bits/stdc++.h> using namespace std; const int N = 4e3+10; const double pi = acos(-1); struct point { int x,y,z,tp; bool operator < (const point & b) const { if (tp!=b.tp) return tp<b.tp; return y*(int64_t)b.x<x*(int64_t)b.y; } bool operator == (const point & b) const { return !(*this<b)&&!(b<*this); } } a[N]; int64_t cross(point a, point b) { return a.x*(int64_t)b.y-a.y*(int64_t)b.x; } int get(int x, int y) { if (x>0&&y>=0) return 1; if (x<=0&&y>0) return 2; if (x<0&&y<=0) return 3; return 4; } int main() { int n; scanf("%d", &n); int64_t sum = 0; for (int i=1; i<=n; ++i) scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z); int cnt = 0; vector<point> f; for (int i=1; i<=n; ++i) { for (int j=1; j<=n; ++j) if (a[j].z>a[i].z) { point u{a[j].x-a[i].x,a[j].y-a[i].y,a[j].z}; u.tp = get(u.x, u.y); f.push_back(u); } } sort(f.begin(),f.end()); f.erase(unique(f.begin(),f.end()),f.end()); if (f.size()<=2) return puts("Y"), 0; for (int i=0; i<f.size(); ++i) if (cross(f[i],f[(i+1)%f.size()])<=0) return puts("Y"), 0; puts("N"); }