Codeforces 1860F 计算几何 / 数学

题意

传送门 Codeforces 1860F Evaluate RBS

题解
计算几何

考虑 a x + b y − z = 0 ax+by-z=0 ax+byz=0,观察到仅当两个平面的交线的两侧,次序交换。更简单地,将 a x + b y ax+by ax+by 看作 ( a , b ) , ( x , y ) (a,b),(x,y) (a,b),(x,y) 的点积,那么 ( a i , b i ) , ( a j , b j ) (a_i,b_i),(a_j,b_j) (ai,bi),(aj,bj) 次序交换仅出现在 ( a i − a j , b i − b j ) (a_i-a_j,b_i-b_j) (aiaj,bibj) 的垂线方向 p p p。枚举 O ( n 2 ) O(n^2) O(n2) 点对,可以得到所有可能出现点次序交换的位置 p p p。将这些位置逆时针排序,依次处理,每次次序发生交换的点一定是位于数个连续区间,那么把这些可能交换的点单独进行排序,每个点对均摊处理 O ( 1 ) O(1) O(1),总时间复杂度 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn)

p p p 两侧,点对的次序固定;恰好 ( x , y ) = p (x,y)=p (x,y)=p 时,数个点的点积相同,此时左括号置于前面(根据括号的性质,所有前缀和非负,则满足条件)。具体实现上,对于相邻位置 p i , p i + 1 p_i,p_{i+1} pi,pi+1 的中间的 ( x , y ) (x,y) (x,y),取 p i + p i + 1 p_i+p_{i+1} pi+pi+1 进行排序。

#include 
using namespace std;
template <typename T>
struct Point {
    T x, y;
    bool operator<(const Point &o) const {
        return det(o) > 0;
    }
    Point operator+(Point o) {
        return {x + o.x, y + o.y};
    }
    T dot(Point o) {
        return x * o.x + y * o.y;
    }
    T det(const Point &o) const {
        return x * o.y - o.x * y;
    }
};
using P = Point<long long>;
struct Value {
    P v;
    int c;
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int tt;
    cin >> tt;
    while (tt--) {
        int n;
        cin >> n;
        vector<Value> val(2 * n);
        for (int i = 0; i < 2 * n; ++i) {
            char op;
            auto &v = val[i].v;
            cin >> v.x >> v.y >> op;
            val[i].c = op == '(' ? 1 : -1;
        }
        map<P, vector<int>> pos;
        for (int i = 0; i < 2 * n; ++i) {
            for (int j = 0; j < i; ++j) {
                auto dx = val[i].v.x - val[j].v.x;
                auto dy = val[j].v.y - val[i].v.y;
                if (dx < 0) {
                    dx *= -1;
                    dy *= -1;
                }
                if (dx <= 0 || dy <= 0) {
                    continue;
                }
                pos[{dy, dx}].push_back(i);
                pos[{dy, dx}].push_back(j);
            }
        }
        for (auto &[_, p] : pos) {
            sort(p.begin(), p.end());
            p.erase(unique(p.begin(), p.end()), p.end());
        }
        const int inf = 1e8;
        P v{inf, 1};
        vector<int> ord(2 * n), rnk(2 * n);
        iota(ord.begin(), ord.end(), 0);
        iota(rnk.begin(), rnk.end(), 0);
        vector<int> pre(2 * n);
        iota(pre.begin(), pre.end(), 0);
        vector<int> sum(2 * n + 1);
        int neg = 0;
        int found = 0;
        auto update = [&](P p, vector<int> &pos) {
            int m = pos.size();
            vector<int> tmp(m);
            for (int i = 0; i < m; ++i) {
                tmp[i] = rnk[pos[i]];
            }
            sort(pos.begin(), pos.end(), [&](int i, int j) {
                auto x = p.dot(val[i].v), y = p.dot(val[j].v);
                if (x != y) {
                    return x < y;
                }
                return val[i].c > val[j].c;
            });
            sort(tmp.begin(), tmp.end());
            for (int i = 0; i < m; ++i) {
                ord[tmp[i]] = pos[i];
                rnk[pos[i]] = tmp[i];
                neg -= sum[tmp[i] + 1] < 0;
                sum[tmp[i] + 1] = sum[tmp[i]] + val[pos[i]].c;
                neg += sum[tmp[i] + 1] < 0;
            }
            if (neg == 0) {
                found = 1;
            }
        };
        update(v, pre);
        pre.clear();
        for (auto &[u, cur] : pos) {
            if (found) {
                break;
            }
            P w = v + u;
            update(w, pre);
            update(u, cur);
            pre = cur;
            v = u;
        }
        cout << (found ? "YES" : "NO") << '\n';
    }

    return 0;
}
数学

与上述计算几何方法原理相同,将 a x + b y ax+by ax+by 化作 a + b x / y a+bx/y a+bx/y,直观上即二维平面的直线随着 t = x / y t=x/y t=x/y 递增不断交换次序。 t = ( a i − a j ) / ( b j − b i ) t=(a_i-a_j)/(b_j-b_i) t=(aiaj)/(bjbi) 时,点 i , j i,j i,j 次序发生交换。预处理出 t → 0 + t\rightarrow 0^{+} t0+ 情况下的次序,不断维护相邻点发生交换的 t t t 值,依次交换即可。总时间复杂度 O ( n 2 log ⁡ n ) O(n^2\log n) O(n2logn)

#include 
using namespace std;
struct Value {
    int a, b, c;
    bool operator<(const Value& o) const {
        if (a != o.a) {
            return a < o.a;
        }
        if (b != o.b) {
            return b < o.b;
        }
        return c > o.c;
    }
};
struct Point {
    long long x, y;
    int d, p;
    bool operator<(const Point& o) const {
        auto t = y * o.x - o.y * x;
        if (t != 0) {
            return t < 0;
        }
        if (d != o.d) {
            return d > o.d;
        }
        return p < o.p;
    }
};

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int tt;
    cin >> tt;
    while (tt--) {
        int n;
        cin >> n;
        n *= 2;
        vector<Value> val(n);
        for (int i = 0; i < n; ++i) {
            char op;
            cin >> val[i].a >> val[i].b >> op;
            val[i].c = op == '(' ? 1 : -1;
        }
        sort(val.begin(), val.end());
        vector<int> sum(n + 1);
        int neg = 0;
        for (int i = 0; i < n; ++i) {
            sum[i + 1] = sum[i] + val[i].c;
            neg += sum[i + 1] < 0;
        }
        set<Point> st;
        auto get = [&](int i) -> Point {
            return {val[i].b - val[i + 1].b, val[i + 1].a - val[i].a, val[i + 1].c - val[i].c, i};
        };
        auto op = [&](int i, bool add) {
            if (0 <= i && i + 1 < n && val[i].b - val[i + 1].b > 0 && val[i + 1].a - val[i].a > 0) {
                if (add) {
                    st.insert(get(i));
                } else {
                    st.erase(get(i));
                }
            }
        };
        for (int i = 0; i + 1 < n; ++i) {
            op(i, true);
        }
        auto eq = [&](const Point& a, const Point& b) {
            return a.x * b.y - b.x * a.y == 0 && a.d == b.d;
        };
        auto judge = [&]() {
            if (neg == 0) {
                return true;
            }
            while (!st.empty()) {
                auto v = *st.begin();
                while (!st.empty() && eq(*st.begin(), v)) {
                    auto u = *st.begin();
                    st.erase(u);
                    int i = u.p;
                    op(i - 1, false);
                    op(i + 1, false);
                    neg -= sum[i + 1] < 0;
                    swap(val[i], val[i + 1]);
                    sum[i + 1] = sum[i] + val[i].c;
                    neg += sum[i + 1] < 0;
                    op(i - 1, true);
                    op(i, true);
                    op(i + 1, true);
                }
                if (neg == 0) {
                    return true;
                }
            }
            return false;
        };
        cout << (judge() ? "YES" : "NO") << '\n';
    }

    return 0;
}

你可能感兴趣的:(计算几何,数学,算法)