极角排序

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);
}
按atan2排序
#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]);
}
View Code

因为只用求直角,可以不极角排序,直接把向量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]);
}
View Code

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");
}
View Code

 

posted @ 2020-11-18 20:34  dz8gk0j  阅读(163)  评论(0编辑  收藏  举报