1. 程式人生 > >幾何入門合集

幾何入門合集

using 能夠 tdi ack ast 極角排序 sta end 處理

F. Mirror

題意

鏈接

三維幾何鏡像問題:
有n個人在y=0的平面上(及xoz平面)。z=0平面上有一面鏡子(邊平行於坐標軸)。z=a平面上有q個點(保證a大於所有人的z坐標)。 所有人面朝鏡子,且在鏡子和q個點之間(即每個人的z坐標保證0<z<a)。
問對於某個點,讓所有人能夠通過鏡子看到那個點的鏡子的最小面積。


題解

首先考慮鏡面,我們可以通過(初中科學的)鏡面反射原理,關於z=0做出z=a的對稱平面z=-a。問題就變成了n個人看z=-a上的某個點。(下圖綠點是人,紅點是詢問點)

技術分享圖片

然後觀察,鏡子的高和寬是獨立的。 於是我們分別求它們的最大值即可。
求高比較簡單,我們朝-x方向看yoz平面。通過把每個人跟點Q的像Q‘相連,我們發現離Q’z軸距離最近的人對應著鏡子的下邊界,最遠的人對應著上邊界,通過維護所有人z坐標的max_z&min_z以及相似三角形可以直接求出兩個邊界,復雜度為O(1)。

我們用同樣的方法,朝-y方向看xoz平面。 通過把每個人跟點Q的像Q‘相連,我們發現,左右邊界並不對應著最左邊與最右邊的人。而且隨著詢問點的變化,對應著左右邊界的人也在變化。
如果我們暴力的找對應左右邊界的人,復雜度為n*q ,不可行。
我們發現,對應著左右邊界的人雖然隨著詢問點變化,但他們都在凸包上(如下圖)。

技術分享圖片

更進一步,如果我們將詢問點按照x坐標排序,隨著詢問的x坐標增加,左右邊界的人在凸包上的變化是順時針旋轉的。(考慮你從左到右觀察一個正前方的凸包)
於是我們就能夠通過一個nlogn的凸包預處理然後O(1)地回答每個詢問,復雜度為O(q+nlogn)

剩下的是實現”從左到右看凸包時凸包左右邊界的順時針更新“。

首先是寫out,in函數(右手法則,向外轉就是逆時針),用來逆時針、順時針遍歷凸包上的點。因為極角排序凸包存的點是逆時針的(極角排序的那個角是與y軸的逆時針夾角。),所以out就是++。
先找到凸包的下上頂點,作為初始的左右邊界的對應點。
然後根據x坐標從小到大枚舉詢問點Q。
對於每個Q,不斷順時針更新左邊界對應的人,直到他與Q的連線在他凸包上順時針的下一個人與Q的連線的外面(直觀上顯然正確)。 右邊界同理。
某些編輯器比如codeforces不能混用iostream與stdio


代碼

#include<cmath>
#include<algorithm>
#include<iostream>
#include<stdio.h>
#include<map>
#include<string.h>
#include<queue>
#include<stack>
using namespace std;
#define debug(x) cerr<<#x<<" = "<<(x)<<endl
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define FAST_IO ios_base::sync_with_stdio(false); cin.tie(nullptr)
//#define double long long
typedef long long ll;
typedef double db;
const int maxn = 2e5 + 5;
const db eps = 1e-7;
int n, q, a;

long double ans[maxn];
bool eq(double a, double b) { return abs(a - b) <= eps; }
struct V {
    double x, y;
    V(double a = 0.0, double b = 0.0) :x(a), y(b) {}
    void sc() { scanf("%lf%lf", &x, &y); }
    double operator |(V const &o)const {
        return x * o.y - o.x * y;
    }
    bool operator <(V const &o)const {
        if (eq(x, o.x))return y < o.y;
        return x < o.x;
    }
    V operator -(V const &o)const { return V(x - o.x, y - o.y); }
    void pr() { printf("%lf %lf\n", x, y); }
}st[maxn];
pair<V, int> Q[maxn];
bool cmpr(V const &a, V const &b) {
    V v1 = a - st[0], v2 = b - st[0];
    return (v1 | v2) < -eps;
}

vector<V> ch;
void getCH() {
    sort(st + 1, st + n, cmpr);
    ch.push_back(st[0]);
    rep(i, 1, n-1) {
        while (ch.size() > 1 && (st[i] - ch.back() | ch.back() - ch[ch.size() - 2]) < eps)ch.pop_back();
        ch.push_back(st[i]);
    }
}
int out(int x) { return x ? x - 1 : ch.size() - 1; }
int in(int x) { return x + 1 == (int)ch.size() ? 0 : x + 1; }


double getx(double xs, double z, double xq) {
    return xs + z / (z + a) * (xq - xs);
}



int main() {
    //FAST_IO;
    int t;
    cin >> t;
    while (t--) {

        cin >> n >> a;
        
        rep(i, 0, n - 1)st[i].sc();
        db zmn = st[0].y, zmx = st[0].y;
        rep(i, 0, n - 1) {
            zmn = min(zmn, st[i].y);
            zmx = max(zmx, st[i].y);
        
        }
        rep(i, 0, n - 1)st[i].y = a - st[i].y;
        sort(st, st + n);
        ch.clear();
        getCH();

        cin >> q;
        ll qx = 0, qy = 0;
        rep(i, 1, q) {
            Q[i].first.sc();
            Q[i].second = i;
        }
        sort(Q + 1, Q + 1 + q);

        int lp = 0, rp = 0;
        rep(i, 0, ch.size() - 1)ch[i].y = a - ch[i].y;
        while (ch[in(rp)].y < ch[rp].y)rp = in(rp);
        while (ch[out(lp)].y > ch[lp].y)lp = out(lp);

        rep(i, 1, q) {
            while (true) {
                int ni = in(lp);
                if (getx(ch[ni].x, ch[ni].y, Q[i].first.x) < getx(ch[lp].x, ch[lp].y, Q[i].first.x))lp = ni;
                else break;
            }
            while (true) {
                int ni = in(rp);
                if (getx(ch[ni].x, ch[ni].y, Q[i].first.x) > getx(ch[rp].x, ch[rp].y, Q[i].first.x))rp = ni;
                else break;
            }
            double x = abs(getx(ch[rp].x, ch[rp].y, Q[i].first.x) - getx(ch[lp].x, ch[lp].y, Q[i].first.x));
            double h = getx(0, zmx, Q[i].first.y) - getx(0, zmn, Q[i].first.y);
            ans[Q[i].second] = (long double)x * h;
            //printf("%.20lf\n", x*h);
        }
        rep(i, 1, q)printf("%.20lf\n", (double)ans[i]);
    }
    cin >> n;
}

/*
1
3 3
-2 1
7 2
3 1
3
2 5
-2 4
8 10
*/


心路歷程

當有兩個以上的bug時你就炸了
wa0:FAST_IO codeforces以用就wa

wa1:輸出rep(i,1,q) not rep(i,1,n)//最後才發現
wa2:凸包板子裏面下標從0開始。

幾何入門合集