1. 程式人生 > 其它 >洛谷 P7090 [NWRRC2013]Lonely Mountain 題解

洛谷 P7090 [NWRRC2013]Lonely Mountain 題解

一、題目:

洛谷原題

codeforces原題

二、思路:

我認為這道題出的很好,不僅考驗了選手的數學素養,也考驗了選手的計算機功底。(反正我沒做出來。)

我們稍加思考就會發現,如果對於每個點(不論是正檢視還是左檢視),我們都在對應的豎座標將這個幾何體“攔腰斬斷”,那麼最終一定會得到一堆層,每層都是若干個“梯形體”。

上、下面平行且為長方形,四個側面都是梯形,由此圍成的立體圖形叫梯形體。

注意梯形體和臺體是有區別的。臺體要求側稜必須交於一點,但是梯形體沒有這個要求。所以,梯形體當然也就不能用臺體體積的計算公式了。

那梯形體的體積怎樣計算呢?兩種方法,一種是分割法,參見下面這張圖;另一種是對梯形體的高積分。

\[V = \dfrac{H}{3} \times \left(AB + ab + \dfrac{Ab + aB}{2} \right) \]

其中,\(A\)\(B\) 是梯形體下底面的長和寬,\(a\)\(b\) 是梯形體上底面的長和寬。\(H\) 是梯形體的高。

我們還有一個性質,如果一堆等高梯形體的上底面和下底面分別能拼成一個大的矩形,那麼我們就可以把最終拼成的兩個大矩形當做一個新梯形體的上下底面,從而能算出這堆梯形體的總體積。

所以這道題就簡單了!我們只需要維護當前層的上下底面的長和寬進行累加即可。長和寬的更新可以用斜率的倒數進行計算。

對了這道題還有一個非常蛋疼的地方,就是豎座標可能會相同。我本來使用隨機抖動法來避免這個問題,但是以失敗告終;只能老老實實特殊處理。具體過程見程式碼。

三、程式碼:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
#define FILEIN(s) freopen(s, "r", stdin);
#define FILEOUT(s) freopen(s, "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)

#define eps 1e-8

inline int read(void) {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int MAXN = 100005;

int tot, n[2];

int changex[2][MAXN], maxx[2];
double delta[2][MAXN];

struct Point {
    int x, y;
}p[2][MAXN];

struct Node {
    int t, h, id;
    inline friend bool operator <(const Node&a, const Node&b) {
        return a.h > b.h;
    }
}height[MAXN << 1];

inline void input(int t) {
    n[t] = read();
    for (int i = 1; i <= n[t]; ++ i) {
        p[t][i].x = read();
        p[t][i].y = read();
        maxx[t] = max(maxx[t], p[t][i].y);
        height[++ tot] = { t, p[t][i].y, i };
    }
}

inline void get_changex(int t) { // 處理豎座標相同的情況。
    for (int i = 2; i <= n[t]; ++ i)
        if (p[t][i].y == p[t][i - 1].y)
            changex[t][i] = p[t][i].x - p[t][i - 1].x;
}

inline void get_delta(int t) {
    for (int i = 2; i < n[t]; ++ i) {
        if (p[t][i].y != p[t][i - 1].y) {
            delta[t][i] += 1.0 * (p[t][i].x - p[t][i - 1].x) / (p[t][i].y - p[t][i - 1].y);
        }
        if (p[t][i].y != p[t][i + 1].y) {
            delta[t][i] += -1.0 * (p[t][i].x - p[t][i + 1].x) / (p[t][i].y - p[t][i + 1].y);
        }
    }
}

inline double get_volume(double a, double b, double A, double B, double H) {
    return H / 3 * (A * B + a * b + 0.5 * (A * b + B * a));
}

int main() {
    input(0);
    get_changex(0);
    get_delta(0);

    input(1);
    get_changex(1);
    get_delta(1);

    if (maxx[0] != maxx[1]) { puts("Invalid plan"); return 0; }

    sort(height + 1, height + tot + 1);

    double L[2] = {0, 0}, k[2] = {0, 0}, nowh = height[1].h, ans = 0;

    for (int i = 1; i <= tot; ++ i) {
        if (nowh != height[i].h) {
            double l[2];
            l[0] = L[0] + k[0] * (nowh - height[i].h);
            l[1] = L[1] + k[1] * (nowh - height[i].h);

            ans += get_volume(L[0], L[1], l[0], l[1], nowh - height[i].h);

            L[0] = l[0]; L[1] = l[1];
            nowh = height[i].h;
        }
        L[height[i].t] += changex[height[i].t][height[i].id];
        k[height[i].t] += delta[height[i].t][height[i].id];
    }
    printf("%.8lf\n", ans);
    return 0;
}